|
In context (embedded programming, which in retrospect is still too big of a field for this comment to make sense by itself; what I meant was embedded programming on devices with very limited RAM or other such significant restrictions), "baggage" is the fact that you don't have many options when converting async high-level code into low-level machine code. The two normal things people write into their languages/compilers/whatever (the first being much more popular, and there do exist more than just these two options) are: 1. Your async/await syntax desugars to a state machine. The set of possible states might only be runtime-known (JS, Python), or it might be comptime-known (Rust, old-Zig, arguably new-Zig if you squint a bit). The concrete value representing the current state of that state machine is only runtime-known, and you have some sort of driver (often called an "event loop", but there are other abstractions) managing state transitions. 2. You restrict the capabilities of async/await to just those which you're able to statically (compile-time) analyze, and you require the driver (the "event loop") to be compile-time known so that you're able to desugar what looks like an async program to the programmer into a completely static, synchronous program. On sufficiently resource-constrained devices, both of those are unworkable. In the case of (1) (by far the most common approach, and the thing I had in mind when arguing that async has potential issues for embedded programming), you waste RAM/ROM on a more complicated program involving state machines, you waste RAM/ROM on the driver code, you waste RAM on the runtime-known states in those state machines, and you waste RAM on the runtime-known boxing of events you intend to run later. The same program (especially in an embedded context where programs tend to be simpler) can easily be written by a skilled developer in a way which avoids that overhead, but reaching for async/await from the start can prevent you from reaching your goals for the project. It's that RAM/ROM/CPU overhead that I'm talking about in the word "baggage." In the case of (2), there are a couple potential flaws. One is just that not all reasonable programs can be represented that way (it's the same flaw with pure, non-unsafe Rust and with attempts to create languages which are known to terminate), so the technique might literally not work for your project. A second is that the compiler's interpretation of the particular control flow and jumps you want to execute will often differ from the high-level plan you had in mind, potentially creating more physical bytecode or other issues. Details matter in constrained environments. |