nrc / portable-interoperable

Async fundamentals initiative: portable and interoperable
Apache License 2.0
74 stars 0 forks source link

async function traits #14

Open nrc opened 2 years ago

nrc commented 2 years ago

See async_fn_traits. Essentially traits for functions which return a future. I'm not clear on why there are versions for different numbers of arguments rather than having a generic parameter for the arguments (like the Fn traits). I'm also not clear on how much these traits are just a convenience vs how much the compiler will need to know about their correspondence with the Fn traits to be useful.

NobodyXu commented 2 years ago

I'm not clear on why there are versions for different numbers of arguments rather than having a generic parameter for the arguments (like the Fn traits).

I suppose that is to work around the lack of variadic generic parameters support in rust.

SabrinaJewson commented 2 years ago

how much the compiler will need to know about their correspondence with the Fn traits to be useful

The compiler will need to know about them to get type inference with HRTBs to work. Today, this doesn’t compile:

fn example<F: for<'a> AsyncFn1<&'a u32, Output = ()>>(func: F) {}
example(|x| async { dbg!(x); });

and that’s because the Fn bound is only indirectly applied via the AsyncFn1 trait and not in the function. However, we can’t move the Fn bound to the function because then we wouldn’t be able to express what would come after the -> (since its type should be able to vary based on the HRTB).

joshtriplett commented 2 years ago

Does this get easier if we stabilize async |x| so that we don't just have "function returning a future", we have "async function"?

SabrinaJewson commented 2 years ago

I mean, potentially. If async |x| were stabilized but it just desugars to |x| async { /* … */ } then obviously not, but I’d imagine it’d be possible to implement it in a “smarter” way, although I’m not a compiler person so I don’t know unfortunately. Another potential solution is to allow -> _ in Fn trait bounds and stabilize FnOnce::Output, so the bound could be written like this:

fn example<F>(func: F)
where
    F: for<'a> Fn(&'a u32) -> _,
    for<'a> <F as Fn(&'a u32) -> _>::Output: Future<Output = ()>,
{}

Or maybe, the compiler could have the example Just Work by propagating the Fn supertrait for type inference correctly.