4 ms·
I don't think any of this is actually a reason why async rust doesn't work. The function color problem is a bit overblown, it hasn't broken nodejs yet either. M
by hctaw 6y ago
I don't think any of this is actually a reason why async rust doesn't work. The function color problem is a bit overblown, it hasn't broken nodejs yet either. Most code is sync, sync can call async and vice versa, and the ecosystem hasn't come tumbling down yet (since most Rust isn't whichever async application runtime for web apps is currently in vogue - its a systems language after all).
Either way there are bigger function "color" problems in Rust.
If you want a target for criticism in async Rust, it should be the lack of easy conversion from generators to async functions (and back) and the cognitive difficulty of implementing a custom executor.
I also think the tangent on closures is nonsense, closures are syntactically and semantically complex because closures are hard to keep safe without a garbage collector. Reference radioactivity is a feature, not a bug.
- TheCoelacanth 6y agoWhy is it a problem that custom executors are difficult to implement? Most other languages that I'm aware of they wouldn't even be possible to implement.
- hctaw 6y agoIt's not really a problem worth caring about. Just if you wanted to gripe about the complexity of async Rust it's the most obvious to me. "But pretty much no one even lets you do that" is a great counter argument. But, it is kind of an anti feature. It's incredible that Rust allows custom async executors and the surface area for them is tiny! That said, it's kind of black magic, even for Rust. I'm willing to bet there's fewer than 20 people in the world that could make a production executor today (not that it's going to stay that way - don't get me wrong). If there's one reason to write an async application today in Rust it's because you can write a custom executor and give it a ridiculous amount of information about how the application is running by communicating with it, while wrapping both callback and polling backed APIs from system libraries in C. But that's really hard to do if writing an executor is harder than just writing callbacks or polling code manually.
- ArchOversight 6y agoSteve Klabnik did a talk about async/await and building your own executor: https://www.infoq.com/presentations/rust-async-await/ https://www.infoq.com/presentations/rust-async-await/ Might be worth a listen if you are intent on building your own!
- thu2111 6y agoIs it really that incredible? Look at how Kotlin implemented coroutines. The language support is tiny - nearly everything is implemented in libraries that you can compete with. In particular the language rejected the async/await model: you still mark functions as async (suspend) but there's no language level equivalent of await. Just lots of library level APIs to compare coroutines in different ways. It's really nice.
- xfer 6y agoI really don't see a problem with function color in a typed language. Every function has color and compiler enforces you pass right color. Async is just another type that may have some convenient syntax. It might be inconvenient in untyped languages like js since you couldn't compose them.
- CyberRabbi 6y agoIt’s not a problem of being error-prone, the problem is that the ecosystem is split. As a crate author, do I implement my functions for color A or color B? Every crate author will eventually encounter this question and the inevitable fall out from making the wrong decision. Presently, the only way to deal with this is implement your functionality in both colors.
- hctaw 6y agoIt's only a question if the function needs to call something else that could be either color. The vast majority of code does not. It would take a couple months of bike shedding syntax and discussing internals but I feel like there's a version of this (applied to other function colors) that allowed a library author to annotate/decorate a function for an inclusive or exclusive set of these decorations the compiler can pick to call from a calling context, and allow the function author to specialize over the combination of decorators that actually get called. Since the set of decorators is small (I can only think of 2-3, including async) this feature would be straightforward to implement albeit potentially verbose. There's a fancy compiler pass to make async work though. You need a special control flow analysis to figure out yield points in async bodies, since this new syntax would have no await. It's probably too late for that. It might be implemented in terms of HKT.
- xfer 6y agoIf your function represents async workflow then you can just implement the async side and the caller can just block on it if they want that semantics. The problem with the ecosystem in Rust is there are various runtime libraries and no standard way to factor it out. You shouldn't need to implement the blocking semantics since it should be trivial.
- valand 6y agoI agree on generator<->async conversion. I'm not an expert but I can't shake the thought that Rust had it backward with async and generator. Generator looks like a good bridge between sync and async. Before reading about implementing async executor and digging under what tokio and async-std is doing (it's been months and I haven't finished), I thought implementing async executor is just as easy as running looping a generator.resume() until it yields GeneratorState::Complete until I realize generator is still on nightly! It may be because I'm a noob in Rust, but I can't write easily: ``` let some_handle_arc = Arc::new(some_handle); future::block_on([ // this future will stop running midway if some_handle is modified in some way make_future(some_handle_arc), // make_future_stopper will modify some_handle_arc when receiving // a certain input from a certain interface make_future_stopper(some_handle_arc) ]); ``` My initial intuition is if async is generator-based, there should be an escape hatch where the generator-runner just exit, drop the generator, without waiting for the generator to yield Generator::Complete. But again it might be from my noobness being a simple Rust hobbyist. Sorry for the rant
- int_19h 6y ago> The function color problem is a bit overblown, it hasn't broken nodejs yet either. I agree; and I would also point at C#, which is the originator of the async/await syntax, and has had it for over 8 years now, with the ecosystem embracing it for most of that period. It works.
- orthoxerox 6y agoI've been waiting for an async Oracle ADO.NET driver for over 8 years now.
- ash 6y ago> The function color problem is a bit overblown, it hasn't broken nodejs yet either. It didn't break Node because Node avoids function color problem altogether. Nobody writes blocking code in Node due to its single-threaded nature. Function color problem is the difficulty of mixing blocking and async functions. It's especially annoying in Python, where you have to spawn a thread to call blocking functions from async functions, and welcome to mutithreading bugs. In Node the problem doesn't exist, because you don't block in its blocking functions. You either do `await f()` or `f().then()`.
- pron 6y agoThe issue is much more general. I also don't see a particular problem with Rust here, but I do see with Rust the same false hope I saw in the late eighties and early nineties with C++ (hell, I fell victim to it myself back then): the belief that with enough features, cleverness, and language complexity we could have the cake and eat it too -- have all the control of a low-level language with the same productivity as a high-level language. Low-level languages, whether they try to appear as high-level ones on the page or not, all suffer from low-abstraction, i.e. the inability to hide internal implementation details behind an API and isolate them from consumers. This makes changing implementation details hard and the program much more rigid, and so increases maintenance cost. The problems with C++ weren't apparent at first, because writing the program the first time is as easy as with a high-level language; the problems start when you change the program over the years. There is no doubt Rust cleans up C++ and solves some particular problems with it, but it doesn't solve this fundamental problem. There are many situations where a low-level language and the control it affords are absolutely necessary, but low-level programmers are aware of the non-negligible price they pay.
- creata 6y ago> Low-level languages... all suffer from low-abstraction, i.e. the inability to hide internal implementation details behind an API... Do you have an example of Rust's inability to hide implementation details compared to, say, Java?