4 ms·
My working theory is that async/await syntax causes more problems than it solves. I've read through the RFC and many of the comments, but I don't see anywhere
by couchand 7y ago
My working theory is that async/await syntax causes more problems than it solves. I've read through the RFC and many of the comments, but I don't see anywhere a cogent argument why the syntax is needed -- that seems to be taken on faith.
I've written and reviewed boatloads of promise-using JS code, a fair amount of promise-using C++ and task-using C#, and some future-using Rust. I've never seen a case where the value added by async/await exceeds the potential for issues.
The goal seems to be make asynchronous code look like synchronous code. In my experience, developers do in fact act like it's synchronous, forgetting the critical fact that it's not. The supposed feature is a massive bug in everyday usage.
EDIT: reading through the RFC and comments, I now realize that async fn is NOT actually being implemented as sugar on top of future-returning functions, but in fact is more like a Python or ES6 generator. There seems to be a disagreement as to whether an async fn is really just a future-returning function: I see the claim made both ways, and haven't found an authoritative source yet. Confusing!
EDIT2: > do notation in a language without a GC is an open research question. Some believe it’s impossible.
I don't understand how GC comes into play. Isn't the do notation simply syntactic sugar for bind sequencing?
- steveklabnik 7y ago> I've never seen a case where the value added by async/await exceeds the potential for issues. There are many cases in which you can write safe code with async/await that you cannot write safely with raw futures. See here: http://aturon.github.io/tech/2018/04/24/async-borrowing/ http://aturon.github.io/tech/2018/04/24/async-borrowing/ > I now realize that async fn is NOT actually being implemented as sugar on top of future-returning functions, but in fact is more like a Python or ES6 generator. There seems to be a disagreement as to whether an async fn is really just a future-returning function: I see the claim made both ways, and haven't found an authoritative source yet. Confusing! The return type of an `async fn` is an `impl Future`. Additionally, you can `.await` on any `Future` inside of an `async fn`. That means it feels like sugar for Futures, which is why people say that. It's a good mental model. However, the current implementation is a generator internally. > I don't understand how GC comes into play. Isn't the do notation simply syntactic sugar for bind sequencing? It is in a language with garbage collection. Things get much more dicey when you don't have those things. For example, Rust has three kinds of closures, not one. You have to deal with references and lifetimes. Rust is generally imperative, and so you have to figure out how break, continue, etc, all work. Due to all of these things and others, the various monadic types Rust have don't actually have the same signatures as each other. I believe there are some more issues, but that's what I've got off the top of my head. That doesn't mean that this is actually impossible; it means that it requires more research to even figure out if it is possible. Rust doesn't have to solve every problem perfectly. There will be other languages in the future. Maybe Rust++ will have this kind of tech.
- couchand 7y agoI had not seen that particular example, thanks for providing the citation. Perhaps I'm just being stubborn here, but allow me a moment. Isn't the core issue there that the executor requires the future to have a lifetime of 'static? Aaron explains it: > ...because they are tossed onto executors like thread pools and hence not tied to any particular stack frame. It's surely a huge undertaking, but wouldn't a more nuanced lifetime analysis solve the underlying problem here? I don't think the issue is that any other way to write this example is unsafe, it's that it's running into a limitation of the borrow checker. What's required of an asynchronous borrow? The same thing required of any borrow: exclusivity. Sure, the async/await lets you write code that "looks synchronous", and gives the lexical cues the compiler needs to enforce the borrow. But does that mean it's the only way? The counterexample in Aaron's article even relies on async/await! That makes me highly suspicious that the need for it was taken as a given and never sufficiently challenged. The core issue in that example is that the borrow needs to be held until some point in the future that can't truly be determined lexically. That says to me that it's not a borrow in the traditional Rust model. It is absolutely a useful borrow to check for, but why go about it backwards by only allowing such chronological borrows in such narrow circumstances? Moreover, the example isn't realistic. The model that should be used for reading from a socket is a stream. > The return type of an `async fn` is an `impl Future`. Well, it is and it isn't. For instance, it appears that one can't await on a plain function that returns an `impl Future`...
- steveklabnik 7y ago> Isn't the core issue there that the executor requires the future to have a lifetime of 'static? Yes, and no. That is one of the factors. Another, and IMHO, bigger factor is that the lifetime needs to be 'static only as the task starts to execute. This is why Pin, etc, came into existence. This is due to self-referentiality. > It's surely a huge undertaking, but wouldn't a more nuanced lifetime analysis solve the underlying problem here? Yes, we're back in the realm of "open research question." > What's required of an asynchronous borrow? The same thing required of any borrow: exclusivity. Most borrows are not exclusive. > That makes me highly suspicious that the need for it was taken as a given and never sufficiently challenged. With respect, it is just as easy for me to say "This makes me highly suspicious that you have not investigated how all of this works and that's why you think it's easy." > The core issue in that example is that the borrow needs to be held until some point in the future that can't truly be determined lexically. Rust's borrows have not been lexical since last year. That's fine, of course! It's not like getting into this stuff is easy. But the team working on this has a ton of experience; multiple PhDs, etc, etc. This is the most investigated feature of Rust ever. Again, yes, maybe it is possible, but it's not within the realm of a "just". Additionally, none of any of the stuff in this comment this solves the original issues with do notation. > it appears that one can't await on a plain function that returns an `impl Future`... Sure you can: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=d35fb7bf16d85fdf38c8ef2128b8d824 https://play.rust-lang.org/?version=nightly&mode=debug&editi...