1. 45
  1. 14

    This seems to hit two (granted, unexpected) cases of zig async:

    • The async-ness of a function which implicitly becomes async (i.e. without callconv(.Async) or similar) is not exposed at comptime.
    • Multiple functions that have the same signature can coerce to the same function pointer, but not the same async function as the compiler generated @Frame() state machines are different. Think &dyn Future<Output=T> vs impl Future<Output=T> in Rust, but without the vtable in the dyn version..

    Misunderstanding the cause of these two conditions seemed to lead them down the path that the functions themselves (not pointers to them) are colorful w.r.t invocation, which isn’t always the case. It can be the case at the root levels where concurrency is done explicitly (e.g. _start to async or clone() to async), but those aren’t the conditions pointed out by the post, nor do they need to be exposed to most zig async users as mentioned in Loris’ post.

    Instead of inferring the properties of zig async functions, the misunderstanding (and conclusions that came out of it) could have been avoided by getting clarification from the zig community or through the project’s github issues. Not the best end-goal answer, but it’s the one that’s the most effective anecdotally at the moment.

    1. 17

      I apologize in advance for giving a lazy answer to this (I’m running a Zig meetup atm), but I’ll just quote my comment from HN, written in reply to this comment.

      That’s exactly it. It just enables code reuse. You still have to think about how your application will behave, but you won’t have to use an async-flavored reimplementaion of another library. Case in point: zig-okredis works in both sync and async applicatons, and I don’t have to maintain two codebases.

      https://github.com/kristoff-it/zig-okredis

      I thought using “colorblind” in the title of my original blogpost would be a clear enough hint to the reader that the colors still exist, but I guess you can never be too explicit.

      1. 8

        I think it’s fair to say that a language has e.g. type inference even if it doesn’t work in every case.

        And it’s also fair to point out that it doesn’t work in every case, although the absolutist framing bugs me a little.

        As for the compiler bugs, yeah, I think everyone who has used Zig are aware of the bugs, and are eagerly waiting for self-hosted compiler to become usable. :)

        1. 18

          Given that the original “what color is your function” post chose to initially demonstrate the problem via passing around first-class functions, and the core issue here was passing around pointers to functions, I’m not sure this really can be passed off as “it has the claimed quality, just not in every case”. Or more concisely: if sync versus async matters for calling functions someone hands you, then I don’t see how a meaningful claim of “colorblind” can be made.

        2. 7

          I took “Zig has no function colors” to mean at compile time. You don’t have to define two versions of a function just to use them in sync vs async contexts. Obviously those have different representations at run-time and if you do meta-programming you’re going to need to know how to call them differently based on their runtime representation. And obviously if you use an async thing in a function (like suspend) that only makes sense for async.

          So, he found compiler bugs and some more things the compiler should figure out at compile time, but what else does this post teach us?

          1. 6

            I think it teaches there needs to be better clarity of messaging.

            First of all, the “color” of functions is a vague concept based on everyone’s reading of a blog post that complained about a few different aspects of async, from issues of implementations that can’t await in a sync context (a real major limitation) to not linking particular syntax (just one person’s opinion, a trade-off at most).

            Second, Zig’s “colorblind” solution sounded like it solved all of it (for whatever “it” was). Zig is getting brought up whenever another language discusses their async implementation, with implication that everyone does it wrong, since Zig apparently could do without “color”.

            So now we have ambiguity of what “color” really is, multiplied by the gap between what Zig was assumed to solve vs what it actually solves.

            1. 3

              there needs to be better clarity of messaging.

              The original article(s) describing Zig’s “colorblindness” were already reasonably lengthy. In reading them some time ago, it was immediately clear to me that they were referring to the fact that programmers don’t have to write the same function twice because the compiler specializes automatically. If they had gone into excruciating detail about how that worked, calling conventions, etc, then somebody else would have complained that the articles were too long. You can’t please everyone, especially folks who are determined to find faults and complain.

              Zig is getting brought up whenever another language discusses their async implementation

              It seems especially unfair to find fault in the Zig community for imprecise feedback from compiler/language implementation non-experts in other communities. The experts in those communities have the obligation of relations with their community members.

          2. 4

            Zig will automatically generate both an async and sync function body from the same source-level definition? That’s incredibly awesome, obviously the correct language design decision, and something that I’ve been upset at other languages (Python, Rust) for getting incorrect for a long time.

            (why “obviously correct”? because forcing you to rewrite the same function twice, with the only difference being the presence of the “async” keyword, in order to use it in both an async and sync context, is literally forcing the programmer to do repetitive, automatable work that can and should be done by a computer, which is an automation machine, for no benefit to the human)

            Maybe Zig is worth trying to learn to fit into my slot for “non-managed-memory systems-programming language” - currently occupied by C, because Rust was too difficult for me to fit my mind around in the small amount of time that I previously had to try to learn it.

            1. 1

              It generates only one function “version” of async/sync, depending on whether the function does anything “async” or not (i.e. uses @frame(), does suspend, does await (which implicitly suspends), calls an async function normally (which implicitly awaits)).

              When you’re switching between an async and synchronous version, changing only the keywords may mean you’re probably not taking advantage of either correctly. For example, spawning a bunch of async tasks and joining their results is fine and efficient; Doing the same with threads naively isn’t such a good idea even if it’s a one-to-one mapping. There’s actual, situational benefit in being explicit about what tools you’re using under what async/sync context.