Convenience for interface types (type annotations in patterns? type operators?)

I'm working on a system that involves a couple kinds of functions:

  • Schedulers, which map a set of immutable parameters to a set of times (or to another scheduler, recursively).
  • Events, which run at the scheduled times and take a set of parameters; either for mutation or side effects.

To be brief - a scheduler runs over all permutations of its parameters, where each parameter refers to an item from some outer collection. An event's parameters are more general, but one of them allows you to query for the same items that were used by its scheduler (in the below example it's called "Get"). If you're familiar with Bevy's systems, both kinds of functions take arbitrary parameters in much the same way.

add_events(
    EventBuilder::schedule(|a: Fetch<&A>| {
        let v = a.value;
        |b: Res<B>, C: Res<C>| {
            v.when_eq(b.value) | v.when_eq(c.value)
        }
    })
    .on_begin(|
        Get(Nested(a, (b, c))): Get<Nested<Fetch<&A>, (Res<B>, Res<C>)>>,
        _misc: Query<Misc>,
    | {
        assert!(a.value == b.value || a.value == c.value);
    })
)

This can lead to types that get pretty complex. They're more intimidating, harder to read at a glance, and are redundant in a way that's a little tedious to refactor (and they make Clippy angry). In this case, I was thinking it would be nice if it were possible to label the types inside the tuple patterns, and have the outer parts of the type be inferred:

    .on_begin(|
        Get(Nested(a: Fetch<&A>, (b: Res<B>, c: Res<C>))),
        _misc: Query<Misc>,
    | {

Alternatively, it would be neat if all of the standard binary operators worked on types (e.g. T + U -> <T as std::ops::Add<U>>::Output). I can imagine implementing In<T> + In<U> -> In<Nested<T, U>>:

    .on_begin(|
        Get(Nested(a, (b, c))): Get<Fetch<&A>> + Get<(Res<B>, Res<C>)>,
        // Potentially this could even work in patterns?
        // Get(a) + Get((b, c)): Get<Fetch<&A>> + Get<(Res<B>, Res<C>)>,
        _misc: Query<Misc>,
    | {

I think the first idea would be more generally useful, particularly in Bevy where you have similar interface types like In (In(num): In<i32> becoming In(num: i32)). The second idea seems like it might be useful in other areas dealing with type-level programming, like the type_num crate.

Are there potential problems with these? Do you have any other ideas that might improve the user-friendliness of interface types like these?

Edit: Also, I tried searching for any existing discussion on type annotations in patterns or type operators but couldn't find any. If someone could link one it's appreciated

this sounds like type ascription

1 Like