Specifically, examine this:
fn foo() void {
bar();
std.log.info("foo() is finished", .{});
}
It looks dead simple, but it's a pain in the butt to use because it's actually async, and you can know neither that nor how to get this function to actually finish without looking inside of bar().Not in a trivial way either. A good async language will never let std.log.info print until/unless bar resumes.
The difference between a "blue foo" and a "red foo" is also subtler than we pretend it is. Both will finish eventually, unless they don't, and both may be paused for as long as the OS wants in any preëmptive operating system.
They could instead require me to explicitly write `await bar();` within foo(), and I could find comfort in the "no hidden control flow" tenet of the language once more. I recognise now that this would break the colour-blindness of their async implementation and that this is a pretty significant trade-off, but my perspective remains that this breaks one of the rules they've laid out on their homepage.
bar() could contain an infinite loop. bar() could call rand() and try to reference the null pointer 1 in n times. bar() could issue forth nasal demons to haunt you. bar() could call os.exit. bar() could recurse until the stack is exhausted. bar() could suspend and not resume. The OS could kill bar() from outside before it returns.
Who knows what shadows lurk within a function's stack frame?
Run it and find out.
The invariant which needs to be preserved here is that if bar() returns, the log will print, and equally important, if bar does not return, the log will not print.
That's what a good async system can preserve without knowing what's inside bar.
foo();
std.log.info("main", .{}); foo();
to _ = async foo();
Then your program will compile and your log is not going to be prevented by Zig's async shenanigans.The details are not fresh, but it went something like: if a function called an async function like so `the_frame = async foo();`, you couldn't really reliably `resume the_frame;` unless you were responsible for all suspend points hit inside `foo`. If `foo` tried to call some other function `bar` you had no idea had a suspend point, you couldn't resume the frame anymore since you'd need to resume the inner frame instead.
So, yeah, they were colorless as in you could call any function you wanted and they don't advertise suspend points or async usage, but they are actually colorful inside and calling the wrong color breaks things.