Live data from Hacker News

Async and Await in Rust: a full proposal

boats.gitlab.io

121–130 of 196 posts

Re: Async and Await in Rust: a full proposal

#121
post #29

This is exciting. It allows programmers to model their problems in code that fully accounts for the parallelism. For comparison, here is async/await in Zig: https://ziglang.org/documentation/master/#Coroutines Zig decided to go the other way - when you async call a function, it does eagerly evaluate until the first suspend point. This is less overhead than immediately suspending, plus it removes the dependency of the…

It's exciting to see so many systems programming languages implement async/await. For a further comparison, Nim works in the same way as Zig when it comes to eager evaluation. Something that I'm particularly proud of when it comes to Nim's async/await implementation is that everything, right down to the macro which defines what `await` means, is implemented in the standard library. The compiler only implements the co…

We did implement it out of tree at first. [0] Because Rust requires annotations on macros to distinguish them from built in syntax, they are less ergonomic to use. In general, we believe in adopting a path of beginning as an out-of-tree macro, and then deciding to elevate that to first class syntax if its clear that they will be extremely commonly used.

[0]: https://github.com/alexcrichton/futures-await

Re: Async and Await in Rust: a full proposal

#122

And yet we somehow don't acknowledge the fact that this is just do-notation for some specific monad--yeah, that powerful abstraction that can't be expressed in Rust because we don't allow higher-order polymorphism. Don't get me wrong, i'm bitter because i feel like Rust really is almost in the right direction for the future of language design. Yet there is a long time before we get a language with a really precise ty…

I don't have time to write a whole essay, so let me just establish my credentials: - I wrote the generic associated types RFC (how Rust will implement higher kinded polymorphism). - I wrote the const generics RFC (the closest Rust will get to dependent types). - I wrote the async/await RFC, as well as the linked blog post. That is to say that I am intimately familiar with how Rust's type system can be extended to sup…

Out of topic. Wow withoutboats you didn't have an account here before? Welcome! That's great to see you coming here and to see you giving many in-depth and very precise answers.

Re: Async and Await in Rust: a full proposal

#123

Earlier quoted context omitted.

> you need to compile each function twice ... This can be wasteful. More wasteful than writing each function twice ? See C# for endless examples of libraries that have manual duplicate X() and XAsync() for every single method. If you compare the two they're almost always have the exact same body, except the async version has "async" and "await" peppered in and calls duplicate XAsync methods (which are implemented the…

I quite dislike the current async trend, so you do not have to sell me the alternatives. I was just describing the status quo. I think that a good compromise would be implicitly inferring the 'async-ness' of template functions instantiation (or whatever they are called in your language of choice) based on a magic continuation parameter, which would also go around the separate compilation issue (assuming the language…

Right, that's exactly how I would expect this to be implemented. The lowest-level IO functions (at the level of making syscalls) mark themselves as supporting async: e.g. "asyncable" which means it can be called either sync or async (which may actually be implemented with different os apis). Every function that calls one of these functions sees the results directly as if it were syncronous but is also marked as "asyncable". This "mark" continues up the call chain, completely transparently to the users (unless they use an escape hatch). Near the very top of the program, the main function, an main event loop, your http server thread, etc, the code chooses to call the first function either synchronously or asynchronously and decide details like which executor to use. If your "main" function calls it async, the entire call stack is compiled to use the async versions, all the way down to the syscall functions

The point is that only at the very top of the call stack or the very bottom should you ever need to care or think about whether code is async, because 99% of the time (every bit of code between main and syscalls... which is almost all of it) that's the only place you should need to care. This can be completely automatic because the compiler already knows where to insert await/async, it only needs to know when to use async (decided by main) and what to call at the end (decided by the syscall funcs).

Re: Async and Await in Rust: a full proposal

#124

I'm somewhat disappointed that rust is going with async style stackless continuations. I'm a huge fan of stackfull coroutines/continuations as they are much more elegant and flexible. The downside is that they need a full stack, but I strongly believe (but can't prove) that rust has enough annotations and lifetime capabilities that it should be able to guarantee single frame allocation (or even no allocation for full…

You emphasize that stackfull coroutines could get down to a single allocation, but I'm not sure exactly what you mean. I could trivially get a stackfull coroutine down to a single allocation, the same way that systems thread do it: by allocating a defined stack size and raising a seg fault when you go over it. The trick that "stackless coroutines" can achieve, and do achieve in Rust, is not just getting a single allo…

By single stack frame I mean exactly that: a single perfectly sized (as it is known at compile time) allocation as opposed to a potentially runtime unbounded list of stack frames requuring a full stack (whatever form that mught take). I think that the optimization of converting the unbound stack to a fixed size stack frame can done for stackfull coroutines iff the continuation of the calling coroutine (i.e. the one that was suspended to activate the curren one) never escapes the coroutine function or any non-mutually recursive function called from the coroutine that the compiler can see though (either via inlineing or interprocedural optimization).

Interestingly this is similar to the analysis required to completely optimize a way the coroutine stack frame allocation. And because rust type system is good at tracking lifetimes and nested scopes, I think it might be possible to extend it ti guarantee this sort of optimization. Unfortunately this way beyond my pay grade.

Re: Async and Await in Rust: a full proposal

#125

And yet we somehow don't acknowledge the fact that this is just do-notation for some specific monad--yeah, that powerful abstraction that can't be expressed in Rust because we don't allow higher-order polymorphism. Don't get me wrong, i'm bitter because i feel like Rust really is almost in the right direction for the future of language design. Yet there is a long time before we get a language with a really precise ty…

I don't have time to write a whole essay, so let me just establish my credentials: - I wrote the generic associated types RFC (how Rust will implement higher kinded polymorphism). - I wrote the const generics RFC (the closest Rust will get to dependent types). - I wrote the async/await RFC, as well as the linked blog post. That is to say that I am intimately familiar with how Rust's type system can be extended to sup…

> Monads as implemented in pure functional programming languages like Haskell cannot usefully abstract over asynchronous and synchronous IO in Rust for a variety of reasons having to do with the way the type system exposes low level details by virtue of Rust being a systems programming language. I do not believe that `do` notation could be a useful mechanism for achieving either the ergonomics or the performance that async/await syntax will have in Rust.

Sorry, but I don't buy it. I had a half-baked, unfinished proposal for an effects system that would have allowed Rust to implement async/await just as efficiently (no stackful coroutines) along with any number of other effects [0]. Maybe it wouldn't have been a good idea due to stretching Rust's complexity budget too far, but that's very different from saying it's impossible. Having watched the development of Rust closely I really think that the design team just didn't understand the theory side well enough to be able explore the design space here. (I'm not being as critical as I might sound, PL theory is hard and the Rust devs have wielded it much more competently than the designers of any other non-research language).

[0] https://internals.rust-lang.org/t/start-of-an-effects-system...

Re: Async and Await in Rust: a full proposal

#126
post #111

Earlier quoted context omitted.

Genuine question: aren't all one shot continuations (like the ones this thread is about) naturally delimited? Is there even such a thing as an undelimited one shot continuation?

Sure there is: take an undelimited continuation API, and restrict the resulting continuations to be called only once. Most use cases still work just fine, and I think it would be strange to call that anything other than "undelimited one-shot continuations". Maybe what you have in mind, though, is the traditional argument, that there's no such thing as undelimited continuations. All continuations are naturally "delimi…

I guess that's what I have in mind. Any practical coroutine API has an explicit coroutine creation and yielding point that act as continuation delimiters.

Re: Async and Await in Rust: a full proposal

#127

And yet we somehow don't acknowledge the fact that this is just do-notation for some specific monad--yeah, that powerful abstraction that can't be expressed in Rust because we don't allow higher-order polymorphism. Don't get me wrong, i'm bitter because i feel like Rust really is almost in the right direction for the future of language design. Yet there is a long time before we get a language with a really precise ty…

I don't have time to write a whole essay, so let me just establish my credentials: - I wrote the generic associated types RFC (how Rust will implement higher kinded polymorphism). - I wrote the const generics RFC (the closest Rust will get to dependent types). - I wrote the async/await RFC, as well as the linked blog post. That is to say that I am intimately familiar with how Rust's type system can be extended to sup…

Didn't mean to bash the lang design team at all, actually i was kinda hoping for you folks to reply interesting and tricky stuff about how things are not so simple.

This is probably only because you didn't develop the answer fully but i still struggle to see how monads (or other structures in that family) couldn't apply here: they aren't implemented in any way and just provide interface (eg not caring about rust at all, the semantic of that feature here is very monadic, it should mostly behave like the CPS monad). Anyway, i'm gonna stop arguing, look at how it is/will be implemented and hopefully wait for that essay of yours, to have a broader picture.

Re: Async and Await in Rust: a full proposal

#128
post #111

Earlier quoted context omitted.

Sure there is: take an undelimited continuation API, and restrict the resulting continuations to be called only once. Most use cases still work just fine, and I think it would be strange to call that anything other than "undelimited one-shot continuations". Maybe what you have in mind, though, is the traditional argument, that there's no such thing as undelimited continuations. All continuations are naturally "delimi…

I guess that's what I have in mind. Any practical coroutine API has an explicit coroutine creation and yielding point that act as continuation delimiters.

Mm, not quite. Racket's greenthreads are incredible, and they don't use explicit yielding. I've always wanted something similar for JS.

Re: Async and Await in Rust: a full proposal

#129

Earlier quoted context omitted.

I don't have time to write a whole essay, so let me just establish my credentials: - I wrote the generic associated types RFC (how Rust will implement higher kinded polymorphism). - I wrote the const generics RFC (the closest Rust will get to dependent types). - I wrote the async/await RFC, as well as the linked blog post. That is to say that I am intimately familiar with how Rust's type system can be extended to sup…

> Monads as implemented in pure functional programming languages like Haskell cannot usefully abstract over asynchronous and synchronous IO in Rust for a variety of reasons having to do with the way the type system exposes low level details by virtue of Rust being a systems programming language. I do not believe that `do` notation could be a useful mechanism for achieving either the ergonomics or the performance that…

The first part of your comment is unresponsive to mine; the last part is pretty rude & factually wrong (we are not motivated to implement an effect system right now; we understand the theory).

Sticking to the first part: an effect system is not what the user I was responded to was talking about. They were talking about building do notation on top of type classes with higher kinded polymorphism, which cannot effectively abstract over the monadic operations in Rust.

Re: Async and Await in Rust: a full proposal

#130

Earlier quoted context omitted.

I don't have time to write a whole essay, so let me just establish my credentials: - I wrote the generic associated types RFC (how Rust will implement higher kinded polymorphism). - I wrote the const generics RFC (the closest Rust will get to dependent types). - I wrote the async/await RFC, as well as the linked blog post. That is to say that I am intimately familiar with how Rust's type system can be extended to sup…

Didn't mean to bash the lang design team at all, actually i was kinda hoping for you folks to reply interesting and tricky stuff about how things are not so simple. This is probably only because you didn't develop the answer fully but i still struggle to see how monads (or other structures in that family) couldn't apply here: they aren't implemented in any way and just provide interface (eg not caring about rust at a…

Here are three problems:

Higher kinded polymorphism results in trivially undecidable type inferences without something like currying; the restrictions needed to support it would be arbitrary and weird given that Rust does not have currying (essentially some rules to reconstruct the restrictions of currying in type operator context).

Instances of both Future and Iterator do not implement the Monad type class as defined in Haskell, because the "fmap" operator does not return the "self" type parameterized by a new type, it returns it own new type. This is because the state machine they represent is leaked through their function signature.

Do notation doesn't work with the imperative control flow that Rust has, which other people have already discussed.

Post reply on HN