5 ms·
> One is they make your functions colored This is definitely a good thing. All computations should me "marked" as total or effectful with various possible effe
by ernst_klim 6y ago
> One is they make your functions colored
This is definitely a good thing. All computations should me "marked" as total or effectful with various possible effects (blocking, async, nondeterministic, possibly non-terminating etc etc).
Reasoning about your program is hard when each computation is a blackbox possibly containing any side-effects which could cause unpredictable changes in the control flow and result in a completely incomprehensible way.
Async/await thing is indeed least sound and ergonomic way of doing this. Monads with monad transformers are a bit better. Algebraic effects are the best in terms of composability, ergonomics and mental overhead, but not here yet (though OCaml may be soon become the first industrial-grade language incorporating them [1])
[1] https://www.youtube.com/watch?v=z8SI7WBtlcA https://www.youtube.com/watch?v=z8SI7WBtlcA
- akvadrako 6y ago> This is definitely a good thing. All computations should me "marked" as total or effectful with various possible effects (blocking, async, nondeterministic, possibly non-terminating etc etc). Marking functions for side-effects would be a good thing but it isn't what function coloring means in this context. An async function and a normal function are semantically the same, they just have different syntaxes and you can't easily call one from another. They can be both be either blocking or non-blocking, especially if they take other functions as arguments.
- nostrademons 6y agoThey're not really the same: you know that an async function may potentially suspend and have other code run before its completion, while a normal function (in the absence of threads and signals) is guaranteed to run atomically, at least as far as your process's memory space is concerned. You also know that the only points at which an async function may suspend are an 'await' statement, and so data invariants that don't cross await statements or other async function calls can be reasoned about as if you had purely sequential code. That's precisely the coloring that makes async useful. Without it you need to explicitly protect all shared data with mutexes or other synchronization primitives. 40+ years of threaded programming has shown that programmers cannot generally be trusted to get this right, and this in an area ripe with bugs.
- sanxiyn 6y agoProgrammers cannot get threading right, but Rust can.
- johnnycerberus 6y agoRust won't magically make your threaded code blocks right. It can just provide you tools to ensure that memory won't leak. It's just 1/10 of the solution.
- sanxiyn 6y agoRust solves threading-specific problems. Yes it's partial, but other problems happen in both threaded code and async code, so that's not a reason to choose one over another.
- steveklabnik 6y agoRust does not ensure that your code is free of memory leaks, to be clear.
- Nullabillity 6y agoYes, the standard library has a bunch of legacy blocking IO stuff. But in general, most modern libraries tend to stick to a convention of: - async (or explicitly Future/Stream): external effects (I/O, interacts with synchronization, whatever) - &mut: local effects - otherwise: pure
- ernst_klim 6y ago> An async function and a normal function are semantically the same But they are not, AsyncIO and BlockingIO are different side effects, thus you have different types of computation, that's exactly what I'm talking about. In languages with monads or algebraic effects these would have different types. You don't say that Lists and Arrays are semantically the same, despite being similar sequential collections, they still have separate types for a reason. Though it's good to be able to abstract over them. And in languages with monads we can parametrize over various effect types by using tagless final approach, which allows us to write computations which could be interpreted in contexts of various effects (in this case, Async and Sync), just as we parametrize containers with types of content (in [1] there is an example of how we can parametrize computation over various async implementations), but still these are different effects. [1] https://kubuszok.com/2019/io-monad-which-why-and-how/#typed-tagless-final-interpreter https://kubuszok.com/2019/io-monad-which-why-and-how/#typed-...
- gpderetta 6y agoI don't think considering the blocking strategy an effect is very useful. Even in async context in complex enough applications functions can call other functions including your own, so async is not enough to guarantee reentrancy. I do agree that parametrizing over the blocking strategy is a great idea, but languages that simply provide an async syntactic marker don't necessarily allow that, and if your language is powerful enough you do not need the annotation in the first place.