1. 36
  1. 15

    The author’s categories of “static” and “dynamic” are extremely confused; this is an article about compile-time type-driven metaprogramming.

    1. 5

      I got the impression that the author was trying to get around the typical static vs dynamic typing debate by slightly changing terminology and focus.

      1. 3

        For me, this was an analysis of what type theory really is. To those of us who learned about types because they had to use them to write programs at work (I am in this group), types are nothing more than things you need to write so that a compiler will compile your code. But types have extremely deep mathematical uses going all the way down to the foundation of math itself.

        When it comes to programming, types also exist along a spectrum of what we can use them for. You can’t compare the type system of languages like Swift / Kotlin / C# to C - they are so much more powerful. So I thought this was just an exploration of that - expressing logic either via the typical runtime execution, or via a type system.

        Using types at all is meta-programming at the end of the day. They are computations that exist before a computation is run.

        1. 2

          I didn’t find the article to say much about type theory. The static/dynamic divide in the article is only superficially related to actual connecting tables between type theory and computation.

          A type-checker is just a program which evaluates type annotations, so that a program annotated with a static type is just a pair of interlocking programs. We can metaprogram without types, too.

        2. 1

          I think it’s an interesting categorization, that jives really well with “static typing” and “dynamic typing”. Static typing means typecheck happens at compile time (so, statically according to this article). It’s just that the meat of the post is concerned with static computations vs dynamic computations, a topic less commonly discussed.

        3. 18

          This article incorrectly states that Zig has “colored” async functions. In reality, Zig async functions do not suffer from function coloring.

          Yes, you can write virtually any software in Zig, but should you? My experience in maintaining high-level code in Rust and C99 says NO.

          Maybe gain some experience with Zig in order to draw this conclusion about Zig?

          1. 5

            Not sure if he changed the text but the article mentions the async color problem such that it could be considered applying generally. But the article doesn’t link that to Zig explicitly or did I miss it?

            It would be fair to mention how Zig solved it as he mentions it for Go.

            1. 9

              This response illustrates the number one reason I am not a fan of Zig: its proponents, like the proponents of Rust, are not entirely honest about it.

              In reality, Zig async functions do not suffer from function coloring.

              This is a lie. In fact, that article, while a great piece of persuasive writing, is also mostly a lie.

              It tells the truth in one question in the FAQ:

              Q: SO I DON’T EVEN HAVE TO THINK ABOUT NORMAL FUNCTIONS VS COROUTINES IN MY LIBRARY?

              No, occasionally you will have to. As an example, if you’re allowing your users to pass to your library function pointers at runtime, you will need to make sure to use the right calling convention based on whether the function is async or not. You normally don’t have to think about it because the compiler is able to do the work for you at compile-time, but that can’t happen for runtime-known values.

              In other words, Zig still suffers from the function coloring problem at runtime. If you do async in a static way, the compiler will be able to cheese the function coloring problem away. In essence, the compiler hides the function coloring problem from you when it can.

              But when you do it at runtime and the compiler can’t cheese it, you still have the function coloring problem.

              I think it is a good achievement to make the compiler able to hide it most of the time, but please be honest about it.

              1. 17

                Calling this dishonest and a lie is incredibly uncharitable interpretation of what is written. Even if you’re right that it’s techncially incorrect, at worst it’s a simplification made in good faith to be able to talk about the problem, no more of a lie than teaching newtoniam mechanics as laws of physics in middle school is a lie because of special relativity, or teaching special relativity in high school is a lie because of general relativity.


                Also, I’m not familiar with zig, but from your description I think you’re wrong to claim that functions are colored. Your refutation of that argument is that function pointers are colored, but functions are a distinct entity from function pointers - and one used much more frequently in most programming languages that have both concepts. Potentially I’m misunderstanding something here though, there is definitely room for subtlety.

                1. 11

                  Calling this a dishonest and a lie is incredibly uncharitable interpretation of what is written.

                  No, it’s not. The reason is because they know the truth, and yet, they still claim that Zig functions are not colored. It is dishonest to do so.

                  It would be completely honest to claim that “the Zig compiler can make it appear that Zig functions are not colored.” That is entirely honest, and I doubt it would lose them any fans.

                  But to claim that Zig functions not colored is a straight up lie.

                  Even if you’re right that it’s techncially incorrect,

                  I quoted Kristoff directly saying that Zig functions are colored. How could I be wrong?

                  at worst it’s a simplification made in good faith to be able to talk about the problem, no more of a lie than teaching newtoniam mechanics as laws of physics in middle school is a lie because of special relativity, or teaching special relativity in high school is a lie because of general relativity.

                  There are simplifications that work, and there are simplifications that don’t.

                  Case in point: relativity. Are you ever, in your life, going to encounter a situation where relativity matters? Unless you’re working with rockets or GPS, probably not.

                  But how likely is it that you’re going to run into a situation where Zig’s compiler fails to hide the coloring of functions? Quite likely.

                  Here’s why: while Kristoff did warn library authors about the function coloring at runtime, I doubt many of them pay attention because of the repetition of “Zig functions are not colored” that you hear all of the time from Andrew and the rest. It’s so prevalent that even non-contributors who don’t understand the truth jump into comments here on lobste.rs and on the orange site to defend Zig whenever someone writes a post about async.

                  So by repeating the lie so much, Zig programmers are taught implicitly to ignore the truthful warning in Kristoff’s post.

                  Thus, libraries get written. They are written ignoring the function coloring problem because the library authors have been implicitly told to do so. Some of those libraries take function pointers for good reasons. Those libraries are buggy.

                  Then those libraries get used. The library users do not pay attention to the function coloring problem because they have been implicitly told to do so.

                  And that’s how you get bugs.

                  It doesn’t even need to be libraries. In my bc, I use function pointers internally to select the correct operation. It’s in C, but if it had been in Zig, and I had used async, I would probably have been burned by it if I did not know that Zig functions are colored.

                  Also, I’m not familiar with zig, but from your description I think you’re wrong to claim that functions are not colored. Your refutation of that argument is that function pointers are colored, but functions are a distinct entity from function pointers - and one used much more frequently in most programming languages that have both concepts. Potentially I’m misunderstanding something here though, there is definitely room for subtlety.

                  You are absolutely misunderstanding.

                  How can function pointers be colored? They are merely pointers to functions. They are data. Data is not colored; code is colored. Thus, function pointers (data that just points to functions) can’t be colored but functions (containers for code) can be.

                  If data could be colored, you would not be able to print the value of the pointer without jumping through hoops, but I bet if you did the Zig equivalent of printf("%p\n", function_pointer); it will work just fine.

                  So if there is coloring in Zig, and Kristoff’s post does admit there is, then it has to be functions that are colored, not function pointers.

                  In Kristoff’s post, there is this comment in some of the example code:

                  // Note how the function definition doesn't require any static
                  // `async` marking. The compiler can deduce when a function is
                  // async based on its usage of `await`.
                  

                  He says “when a function is async…” An async/non-async dichotomy means there is function coloring.

                  What the compiler does is automagically detect async functions (as Kristoff says) and inserts the correct code to call it according to its color. That doesn’t mean the color is gone; it means that the compiler is hiding it from you.

                  For a language whose designer eschews operator overloading because it hides function calls, it feels disingenuous to me to hide how functions are being called.

                  All of this means that Zig functions are still colored. It’s just that, at compile time, it can hide that from you. At runtime, however, it can’t.

                  And that is why Zig functions are colored.

                  1. 7

                    I have a hard time following all the animosity in your replies. Maybe I’m just not used to having fans on the internet :^)

                    In my article, and whenever discussing function coloring, I, and I guess most people, define “function coloring” the problem of having to mark functions as async and having to prepend their invocation with await, when you want to get their result. The famous article by Bob Nystrom, “What Color is Your Function?” also focuses entirely on the problem of syntactical interoperability between normal, non-async code and async, and how the second infects codebases by forcing every other function to be tagged async, which in turn forces awaits to be sprinkled around.

                    In my article I opened mentioning aio-libs, which is a very clear cut example of this problem: those people are forced to reinvent the wheel (ie reimplement existing packages) because the original codebases simply cannot be reasonably used in the context of an async application.

                    This is the problem that Zig solves. One library codebase that, with proper care, can run in both contexts and take advantage of parallelism when available. No async-std, no aio-libs, etc. This works because Zig changes the meaning and usage of async and await compared to all other programming languages (that use async/await).

                    You seem to be focused on the fact that by doing async you will introduce continuations in your program. Yes, you will. Nobody said you won’t. What you define as “cheesing” (lmao) is a practical tool that can save a lot of wasted effort. I guess you could say that levers and gears cheesed the need for more physical human labor, from that perspective.

                    Sure, syntax and the resulting computational model aren’t completely detached: if you do have continuations in your code, then you will need to think about how your application is going to behave. Duh, but the point is libraries. Go download OkRedis. Write an async application with it, then write a blocking applicaton with it. You will be able to do both, while importing the same exact declarations from my library, and while also enjoying speedups in the async version, if you allowed for concurrent operations to happen in your code.

                    But how likely is it that you’re going to run into a situation where Zig’s compiler fails to hide the coloring of functions? Quite likely.
                    Thus, libraries get written. They are written ignoring the function coloring problem because the library authors have been implicitly told to do so. Some of those libraries take function pointers for good reasons. Those libraries are buggy.

                    No. Aside from the fact that you normally just pass function identifiers around, instead of pointers, function pointers have a type and that type also tells you (and the compiler) what the right calling convention is. On top of that, library authors are most absolutely not asked to ignore asyncness. In OkRedis I have a few spots where I explicitly change the behavior of the Redis client based on whether we’re in async mode or not.

                    The point, to stress it one last time, is that you don’t need to have two different library codebases that require duplicated effort, and that in the single codebase needed, you’re going to only have to make a few changes to account for asyncness. In fact, in OkRedis I only have one place where I needed to account for that: in the Client struct. Every other piece of code in the entire library behaves correctly without needing any change. Pretty neat, if you ask me.

                    1. 2

                      I have a hard time following all the animosity in your replies. Maybe I’m just not used to having fans on the internet :^)

                      The “animosity” (I was more defending myself vigorously) comes from Andrew swearing at me and accusing me, which he might have had a reason.

                      In his post, he claimed I said he was maliciously lying, but I only said that he was lying. I separate unintentional lies from intentional lies, and I believe all of you are unintentionally lying. Because I realized he thought that, I made sure to tell him that and tell him what I would like to see.

                      In my article, and whenever discussing function coloring, I, and I guess most people, define “function coloring” the problem of having to mark functions as async and having to prepend their invocation with await, when you want to get their result. The famous article by Bob Nystrom, “What Color is Your Function?” also focuses entirely on the problem of syntactical interoperability between normal, non-async code and async,

                      In Bob Nystrom’s post, this is how he defined function coloring:

                      The way you call a function depends on its color.

                      That’s it.

                      Most people associate color with async and await because that’s how JavaScript, the language from his post, does it. But that’s not how he defined it.

                      After playing with Zig’s function pointers, I can say with confidence that his definition, “The way you call a function depends on its color,” does apply to Zig.

                      and how the second infects codebases by forcing every other function to be tagged async, which in turn forces awaits to be sprinkled around.

                      This is what Zig does better. It limits the blast radius of async/await. But it’s still there. See the examples from my latest reply to Andrew. I had to mark a call site with @asyncCall, including making a frame. But then, I couldn’t call the blue() function because it still wasn’t async. So if I were to make it work, I would have to make blue() async. And I could do that while still making the program crash half the time.

                      (Side note: I don’t know how to write out the type of async function. Changing blue() to async is not working with the [2]@TypeOf(blue) trick that I am using. It’s still giving me the same compile error.)

                      In my article I opened mentioning aio-libs, which is a very clear cut example of this problem: those people are forced to reinvent the wheel (ie reimplement existing packages) because the original codebases simply cannot be reasonably used in the context of an async application.

                      This is the problem that Zig solves. One library codebase that, with proper care, can run in both contexts and take advantage of parallelism when available. No async-std, no aio-libs, etc. This works because Zig changes the meaning and usage of async and await compared to all other programming languages (that use async/await).

                      This is not what you are telling people, however. You are telling them that Zig does not have function colors. Those two are orthogonal.

                      And I also doubt that Zig actually solves that problem. I do not know Zig, and it took me all of 30 minutes to 1) find a compiler bug and 2) find an example where you cannot run code in both contexts.

                      You seem to be focused on the fact that by doing async you will introduce continuations in your program. Yes, you will. Nobody said you won’t. What you define as “cheesing” (***) is a practical tool that can save a lot of wasted effort. I guess you could say that levers and gears cheesed the need for more physical human labor, from that perspective.

                      I have no idea what swear word you used there (I have a filter that literally turns swear words into three asterisks like you see there), but this is why I am not happy with Andrew. Now, I am not happy with you.

                      I used “cheesing” because while it is certainly a time saver, it’s still cheating. Yes, levers and gears cheese the application of force. That’s not a bad thing. Computers are supposed to be mental levers or “bicycles for the mind.” Cheesing is a good thing.

                      And yes, I am focused on introducing continuations into the program because there is a better way to introduce continuations and still get concurrency.

                      In fact, I am going to write a blog post about that better way. It’s called structured concurrency, and it introduces continuations by using closures to push data down the stack.

                      Sure, syntax and the resulting computational model aren’t completely detached: if you do have continuations in your code, then you will need to think about how your application is going to behave. Duh, but the point is libraries. Go download OkRedis. Write an async application with it, then write a blocking applicaton with it. You will be able to do both, while importing the same exact declarations from my library, and while also enjoying speedups in the async version, if you allowed for concurrent operations to happen in your code.

                      Where’s the catch? There’s always a catch. Please tell me the catch.

                      In fact, this whole thing is about me asking you, Andrew, and the others to be honest about what catches there are in Zig’s async story.

                      Likewise, I’m going to have to be honest about what catches there are to structured concurrency, and you can hold me to that when the blog post comes out.

                      No. Aside from the fact that you normally just pass function identifiers around, instead of pointers, function pointers have a type and that type also tells you (and the compiler) what the right calling convention is.

                      That is just an admission that functions are colored, if they have different types.

                      On top of that, library authors are most absolutely not asked to ignore asyncness. In OkRedis I have a few spots where I explicitly change the behavior of the Redis client based on whether we’re in async mode or not.

                      They are not explicitly asked. I said “implicitly” for a reason. “It’s not what programming languages do, it’s what they [and their communities] shepherd you to.” By telling everyone that Zig does not have function colors, you are training them to not think about it, even the library authors. As such, you then have to find those library authors, tell them to think about it, and explain why. It would save you and Andrew time if you just were upfront about what Zig does and does not do. And you would have, on average, better libraries.

                      The point, to stress it one last time, is that you don’t need to have two different library codebases that require duplicated effort, and that in the single codebase needed, you’re going to only have to make a few changes to account for asyncness. In fact, in OkRedis I only have one place where I needed to account for that: in the Client struct. Every other piece of code in the entire library behaves correctly without needing any change. Pretty neat, if you ask me.

                      That is neat. I agree. I just want Zig users to understand that, not be blissfully unaware of it.

                      1. 1

                        The “animosity” (I was more defending myself vigorously) comes from Andrew swearing at me and accusing me, which he might have had a reason.

                        You called me a liar in the first comment you wrote.

                        Where’s the catch? There’s always a catch. Please tell me the catch.

                        Since I’m such a liar, why don’t you write some code and show me, and everyone else, where the catch is.

                        1. 1

                          Since I’m such a liar, why don’t you write some code and show me, and everyone else, where the catch is.

                          Well, I don’t need to write code, but I can use your own words. You said that, “Every suspend needs to be matched by a corresponding resume” or there is undefined behavior. When asked if that could be a compiler warning, you said, “That’s unfortunately impossible, as far as I know.”

                          That’s the catch.

                          1. 2

                            Why would you even use suspend and resume in a normal application? Those are low level primitives. I didn’t use either in any part of my blog post, and in fact you won’t find them inside OkRedis either. Unless you’re writing an event loop and wiring it to epoll or io_uring, you only need async and await.

                            This is not a philosophical debate: talk is cheap, as they say, so show me the code. I showed you mine, it’s OkRedis.

                            1. 1

                              Why would you even use suspend and resume in a normal application? Those are low level primitives.

                              Then why are they the first primitives you introduce to new users in the Zig documentation? They should have been last, with a clear warning about their caveats, if you even have them in the main documentation at all.

                              This is not a philosophical debate: talk is cheap, as they say, so show me the code. I showed you mine, it’s OkRedis.

                              I’m not going to download OkRedis or write code with it. I only learned enough Zig to make my examples to Andrew compile, and I have begun to not like Zig at all. It’s confusing and a mess, in my opinion.

                              But if you think that the examples I gave Andrew are not good enough, I don’t know what to tell you. I guess we’ll see if they are good enough for the people that read my blog post on it.

                              But I do have another question: people around Zig have said that its async story does not require an event loop, but none have explained why. Can you explain why?

                              1. 3

                                Then why are they the first primitives you introduce to new users in the Zig documentation? They should have been last, with a clear warning about their caveats, if you even have them in the main documentation at all.

                                They’re the basic building block used to manipulate async frames (Zig’s continuations). First you complained that my blog post didn’t talk about how async frames work, and that I meant to deceive people by not talking about it, then you read the language reference and say it should not even mention the language features that implement async frames.

                                With your attitude in this entire discussion, you put yourself in a position where you have an incentive to not understand things, even well established computer science concepts such as continuations. If we talk at a high level, it’s a lie, if we get into the details, it’s confusing (and at this point we know what you mean to say: designed to be confusing). I can’t help you once you go there.

                                I’m looking forward to reading your blog post, although in all frankness you should consider doing some introspection before diving into it.

                                1. 1

                                  They’re the basic building block used to manipulate async frames (Zig’s continuations). First you complained that my blog post didn’t talk about how async frames work, and that I meant to deceive people by not talking about it, then you read the language reference and say it should not even mention the language features that implement async frames.

                                  That’s the language reference? I thought it was the getting started documentation. Those details are not good to put in documentation for getting started, but I agree that they are good for a language reference. I would still put them last, though.

                                  With your attitude in this entire discussion, you put yourself in a position where you have an incentive to not understand things, even well established computer science concepts such as continuations.

                                  That’s a little ad hominem. I can understand continuations and not understand how they are used in Zig because the language reference is confusing. And yes, it is confusing.

                                  If we talk at a high level, it’s a lie, if we get into the details, it’s confusing

                                  It turns out that the problem is in your documentation and in your blog post. You can talk about it at a high level as long as your language about it is accurate. You can talk about the low level details once the high level subtleties are clarified.

                                  (and at this point we know what you mean to say: designed to be confusing). I can’t help you once you go there.

                                  I do not believe Zig was designed to be confusing, but after using it, I can safely say that the language design was not well done to prevent such confusion.

                                  As an example, and as far as I understand at the moment, the way Zig “gets around” the function colors problem is to reuse the async and await keywords slightly differently than other languages and uses suspend to actually make a function async. So in typical code, async and await do not have the function coloring problem. Which is great and all, but the subtleties of using them are usually lost on programmers coming from other languages.

                                  When I first heard about Zig, by the way, I was excited about it. This was back in 2018, I think, during the part of its evolution where it had comptime but not much more complexity above C. I thought comptime was great (that opinion has changed, but that’s a different story), and that the language looked promising.

                                  Fast forward to today: Zig is immensely more complex than it was back then, and I don’t see what that complexity has bought.

                                  That’s not a problem in and of itself, but complexity does make things harder, which means the documentation should be clearer and more precise. And the marketing should be the same.

                                  My beef with Zig boils down to those things not happening.

                                  Well, okay, I do have another beef with Zig: it sets the wrong tone. Programming languages, once used, set the tone for the industry, and I think Zig sets the wrong tone. So does Rust for that matter. But I can talk about that more in my blog post.

                                  I’m looking forward to reading your blog post, although in all frankness you should consider doing some introspection before diving into it.

                                  I have done introspection. I’ve learned where the function coloring problem actually is in Zig, and I’ve adopted new language to not come off in the wrong way. And I’ll do that in my blog post.

                2. [Comment removed by moderator pushcx: Not only is cursing at someone not a rebuttal, it's unacceptably rude.]

                  1. 6

                    See my reply to @gpm as to why it’s not a lie.

                    But since you called me out on it, I think I will do a public demonstration so that everyone can see why.

                    Here’s the code:

                    const std = @import("std");
                    const RndGen = std.rand.DefaultPrng;
                    
                    var ready: bool = false;
                    
                    fn red(millis: u64) void {
                        suspend {}
                        std.time.sleep(millis);
                        ready = true;
                    }
                    
                    fn blue(millis: u64) void {
                        std.time.sleep(millis);
                        ready = true;
                    }
                    
                    const fns = [2]@TypeOf(blue) { red, blue };
                    
                    pub fn main() !void {
                    
                        const stdout = std.io.getStdOut().writer();
                        try stdout.print("{s}, {s}\n", .{red, blue});
                    
                        var rnd = RndGen.init(@bitCast(u64, std.time.milliTimestamp()));
                        var rand = rnd.random();
                    
                        var millis = rand.uintAtMost(u64, 2000);
                        while (millis < 1000) {
                            millis = rand.uintAtMost(u64, 2000);
                        }
                    
                        var b = rand.boolean();
                        var func: u32 = if (b) 1 else 0;
                    
                        if (func == 1) {
                            try stdout.print("Calling blue\n", .{});
                        }
                        else {
                            try stdout.print("Calling red\n", .{});
                        }
                    
                        fns[func](millis);
                    
                        if (!ready) {
                            std.os.abort();
                        }
                    }
                    

                    It’s saved as test.zig. I’m compiling it with:

                    zig build-exe test.zig
                    

                    using the Zig 0.9.0 package in Gentoo on x64_64.

                    Half the time, it succeeds just fine. The other half, it segfaults trying to call red()!

                    Please tell me how red and blue are not colored when Zig code cannot call them the same way?

                    I am not full of garbage. Zig functions are colored.

                    Side note: it was really painful getting started in Zig. I hate how the compiler won’t let me use tabs, among other things.

                    1. [Comment removed by moderator pushcx: Not only is cursing at someone not a rebuttal, it's unacceptably rude.]

                      1. 17
                        Hey!

                        @andrewrk, thank you for creating and working on Zig. I know it sucks to have people say hyperbolic things about something you put so much effort into, and especially to draw comparisons with people or actions that you might find offensive.

                        @gavinhoward, good work finding a compiler bug and also good work trying to help mitigate what you perceive as some of the obnoxiousness language proponents and missionaries can fall into. I know is sucks to feel like people in their zealotry to promote something are engaging in dishonest or market-y behavior (see my continued skirmishes with the Rust Evangelion Strike Force on this very site).

                        That said, please take a second to back up and cool off here. @gavinhoward, you came in here kinda pissed off about something that turns out to be an acknowledged compiler bug. @gavinhoward, you understandably got pissed off with somebody shooting off complaints about your project and then jumped quickly to matching that nastiness.

                        I know you are all both reasonable people and this is probably just a bad series of coincidences. Please stop being mean to each other here.

                        1. 11

                          I would like to clarify that I don’t mind complaints about Zig. What riled me up is:

                          • being called a liar
                          • spreading of misinformation

                          Regardless, I will cool it. Thanks for moderating. I know it is often unappreciated, tedious work.

                          1. 10

                            Understood–been there myself. Thanks for taking a second.

                            Thanks for moderating. I know it is often unappreciated, tedious work.

                            I’m not a moderator…just somebody who’s been here a bit and participated in my fair share of flamewars. Thank you again for helping not perpetuate that sort of thing.

                          2. 5
                            1. 4

                              I would like to clarify that I don’t mind Zig. What riled me up is:

                              • Hearing people claim things about Zig that are not true.
                              • People claiming I am spreading misinformation when I show up with examples showing otherwise.
                              • Being sworn at.

                              I actually did not come out swinging, or did not mean to. I merely said that they were lying, which to me, meant that they were unintentionally lying, which is definitely excusable in my eyes. But it was a bad choice of words since others saw it differently. I will choose a better phrase next time.

                              That was my only offense before being sworn at.

                              1. 12

                                unintentionally lying

                                I’m not a native speaker, but this is an oxymoron in my book and also a poor post-rationalization after you’ve implied multiple times that my writing is designed to be deceitful. Don’t pull your hand back after you swing, own it.

                                This is a lie. In fact, that article, while a great piece of persuasive writing, is also mostly a lie.

                                No, it’s not. The reason is because they know the truth, and yet, they still claim that Zig functions are not colored. It is dishonest to do so.

                                But to claim that Zig functions not colored is a straight up lie.

                                1. 2

                                  It can still be unintentional, even if you know the truth, because of excitement or any other emotional reason. It can still be dishonest and not be malicious. I stand by that.

                                  However, as I told @friendlysock, I will choose better words next time. What I learned today is that people use the word “misinformation” to avoid using the word “lie”. I guess I need to adopt that practice, much as I do not like it.

                                  1. 8

                                    I know this is several hours too late but this might help keep future threads cooler.

                                    The verb to “lie” means to deliberately mislead. It doesn’t have anything to do with being malicious but it comes with intent, so it can’t be unintentional.

                                    “misinformation” is also intentional misleading

                                    Some alternatives that don’t ascribe intent, that allow you to refute a claim without accusing people: “false”, “incorrect”, “wrong”, “inaccurate”

                                    1. 2

                                      Good to know that misinformation is also not a good word.

                                      However, while you are correct that there are definitions that cover intentional lying, Mirriam-Webster has definitions that cover unintentional lies. 1

                                      See “lie” (verb 2) definition 2 and “lie” (noun 2) definition 1b and definition 2.

                                      1. 3

                                        The verb form is intentional. You can repeat a lie (noun) without the intent to mislead, but to utter a lie (verb) implies intent.

                                        1. 2

                                          I disagree there. To create a false or misleading impression does not require intent. And language is more about how it is used by people than dictionaries, so what I meant is what I meant. I stand by the fact that I meant that they were not doing it on purpose. At least at first.

                                          Now, obviously, language is more about how it is used by people, so I still need to do better next time. I know what I mean, but that doesn’t mean my communication could not be better by eschewing use of the word unless I am arguing intent. And I will do that next time.

                                          1. 8

                                            One way of handling this is to use “subtlety” or “inaccuracy”. Using “lie”, however you personally define it, tends to be interpreted as a deliberate action.

                                            Examples:

                                            • “But to claim that Zig functions not colored is an inaccuracy.”
                                            • “In fact, that article, while a great piece of persuasive writing, overlooks a subtlety in the coloring of Zig functions.”

                                            It’s always useful to hedge a little and give the benefit of a doubt–“so and so is lying” closes that off.

                                            Now please, let’s kill this subthread (@kristoff, if you would too kindly). Lobsters has messages if you want to dig into this further. Doing so will stop sucking air out of the room.

                                2. 9

                                  I will choose a better phrase next time.

                                  Thank you for reflecting on how you came across here.

                              2. 4

                                Congratulations, you have found a bug in the compiler,

                                That does not inspire confidence.

                                because this is supposed to fail with the following compile error:

                                ./test.zig:40:14: error: function is not comptime-known; @asyncCall required fns[func](millis);

                                Good to know.

                                You are full of garbage because your definition of “function coloring” is incoherent, and does not match anyone else’s definition,

                                Let’s go back to the original blog post by Bob Nystrom and see what it says. It has these five points:

                                1. Every function has a color.
                                2. The way you call a function depends on its color.
                                3. You can only call a red function from within another red function.
                                4. Red functions are more painful to call.
                                5. Some core library functions are red.

                                That seems to be the definition people use. Let’s go through them.

                                1. Every function has a color.

                                That’s what we are trying to establish. Do Zig functions have color?

                                1. The way you call a function depends on its color.

                                This is the definition of coloring that most people use. If this point applies to Zig, then Zig’s functions are colored.

                                1. You can only call a red function from within another red function.
                                2. Red functions are more painful to call.

                                These two do not define what function coloring is, but together, they are why people don’t like function coloring.

                                Point 3 is the more painful one, since it infects the entire codebase (usually). Point 4 is easier and is usually done by requiring special syntax.

                                1. Some core library functions are red.

                                I don’t know what Zig’s standard library is like, and I think this one is not relevant to the discussion.

                                So the definition of function coloring is one of the five original points from the post that defines function coloring:

                                1. The way you call a function depends on its color.

                                Do you have to call Zig functions differently based on async? Let’s look at the error message you said should have happened:

                                ./test.zig:40:14: error: function is not comptime-known; @asyncCall required fns[func](millis);

                                Looks like you have to call a function differently by its color, so point 2 does apply to Zig.

                                In fact, let’s look at point 4 (from the function coloring post) by adding @asyncCall. The call site now looks like this:

                                var bytes: [64]u8 align(@alignOf(@Frame(red))) = undefined;
                                const f = @asyncCall(&bytes, {}, fns[func], .{&millis});
                                

                                That looks more painful to me.

                                Now let’s look at point 3 from the function coloring post by trying to compile the changes I mentioned above. The full code looks like this:

                                const std = @import("std");
                                const RndGen = std.rand.DefaultPrng;
                                
                                var ready: bool = false;
                                
                                fn red(millis: *u64) void {
                                    suspend {}
                                    std.time.sleep(millis.*);
                                    ready = true;
                                }
                                
                                fn blue(millis: *u64) void {
                                    std.time.sleep(millis.*);
                                    ready = true;
                                }
                                
                                const fns = [2]@TypeOf(blue) { red, blue };
                                
                                pub fn main() !void {
                                
                                    const stdout = std.io.getStdOut().writer();
                                    try stdout.print("{s}, {s}\n", .{red, blue});
                                
                                    var rnd = RndGen.init(@bitCast(u64, std.time.milliTimestamp()));
                                    var rand = rnd.random();
                                
                                    var millis = rand.uintAtMost(u64, 2000);
                                    while (millis < 1000) {
                                        millis = rand.uintAtMost(u64, 2000);
                                    }
                                
                                    var b = rand.boolean();
                                    var func: u32 = if (b) 1 else 0;
                                
                                    if (func == 1) {
                                        try stdout.print("Calling blue\n", .{});
                                    }
                                    else {
                                        try stdout.print("Calling red\n", .{});
                                    }
                                
                                    var bytes: [64]u8 align(@alignOf(@Frame(red))) = undefined;
                                    const f = @asyncCall(&bytes, {}, fns[func], .{&millis});
                                
                                    if (!ready) {
                                        std.os.abort();
                                    }
                                
                                    resume f;
                                }
                                

                                (I added the resume f; because Kristoff says that it’s UB without it.)

                                Anyway, the compiler spat out this error:

                                ./test.zig:43:41: error: expected async function, found 'fn(*u64) void'
                                    const f = @asyncCall(&bytes, {}, fns[func], .{&millis});
                                                                        ^
                                ./test.zig:43:15: note: referenced here
                                    const f = @asyncCall(&bytes, {}, fns[func], .{&millis});
                                              ^
                                /usr/lib/zig/std/start.zig:553:40: note: referenced here
                                            const result = root.main() catch |err| {
                                

                                It says “expected async function.” That looks like I cannot use a blue function where I have a red one, so yes, calling functions of different colors are different.

                                So let’s do an experiment and turn blue(), but I’m going to do it in a sneaky way: I’m going to make it call another async function and resume it.

                                That looks like this:

                                const std = @import("std");
                                const RndGen = std.rand.DefaultPrng;
                                
                                var ready: bool = false;
                                
                                fn red(millis: *u64) void {
                                    suspend {}
                                    std.time.sleep(millis.*);
                                    ready = true;
                                }
                                
                                fn blue_ish(millis: *u64) void {
                                    suspend {}
                                    std.time.sleep(millis.*);
                                }
                                
                                fn blue(millis: *u64) void {
                                    var bytes: [64]u8 align(@alignOf(@Frame(blue_ish))) = undefined;
                                    const f = @asyncCall(&bytes, {}, blue_ish, .{millis});
                                    resume f;
                                    ready = true;
                                }
                                
                                const fns = [2]@TypeOf(blue) { red, blue };
                                
                                pub fn main() !void {
                                
                                    const stdout = std.io.getStdOut().writer();
                                    try stdout.print("{s}, {s}\n", .{red, blue});
                                
                                    var rnd = RndGen.init(@bitCast(u64, std.time.milliTimestamp()));
                                    var rand = rnd.random();
                                
                                    var millis = rand.uintAtMost(u64, 2000);
                                    while (millis < 1000) {
                                        millis = rand.uintAtMost(u64, 2000);
                                    }
                                
                                    var b = rand.boolean();
                                    var func: u32 = if (b) 1 else 0;
                                
                                    if (func == 1) {
                                        try stdout.print("Calling blue\n", .{});
                                    }
                                    else {
                                        try stdout.print("Calling red\n", .{});
                                    }
                                
                                    var bytes: [64]u8 align(@alignOf(@Frame(red))) = undefined;
                                    const f = @asyncCall(&bytes, {}, fns[func], .{&millis});
                                
                                    if (!ready) {
                                        std.os.abort();
                                    }
                                
                                    resume f;
                                }
                                

                                I get this compiler error:

                                ./test.zig:50:41: error: expected async function, found 'fn(*u64) void'
                                    const f = @asyncCall(&bytes, {}, fns[func], .{&millis});
                                                                        ^
                                ./test.zig:50:15: note: referenced here
                                    const f = @asyncCall(&bytes, {}, fns[func], .{&millis});
                                              ^
                                /usr/lib/zig/std/start.zig:553:40: note: referenced here
                                            const result = root.main() catch |err| {
                                

                                It looks like blue() is still not async. So what this means is that, in Zig, blue functions can call red functions. Point 4 does not apply. Yay!

                                Does this mean that there are no function colors? Heavens, no. I still get that compiler error saying that I can’t mix the two.

                                What it does mean is that Zig’s function color problem is different to JavaScript’s. It’s like JavaScript in that you can’t mix the two, but it’s unlike JavaScript in that it complains at compile time instead.

                                Once again, I am not full of garbage. I compared Zig directly to the definition proposed in the original function coloring blog post, so my definition is not incoherent, and Zig has function colors.

                                and instead of considering that you might be thinking about things from a limited perspective, you arrogantly claim that I and others are maliciously lying.

                                I did not say that you are maliciously lying. I said that you were lying. There is a difference.

                                There are intentional lies, what you call “malicious,” and there are unintentional lies. I thought you and the others are unintentionally lying, probably because of excited evangelism gone a touch too far.

                                That is okay, actually. I have no problem with that. It’s okay to be excited, to like what you made, to want other people to use it. I would want the same, and I think I would make the same mistake too.

                                In fact, in my original post, I almost said,

                                This is a lie, and you know it.

                                But I did not say that because it would imply that you knew you were lying, which would mean you were lying maliciously. And I did not want to assume that.

                                However, then you doubled down.

                                Doubling down is not okay. Doubling down means being criticized, and thus becoming aware of the lie, and deciding to go with the lie anyway. At that point, it is intentional.

                                That’s what you did, so now, I could be convinced that your lie was intentional. Please prove me wrong by accepting criticism.

                                I would be happy if you and your team said, “Zig has function colors, but the compiler does most of the work.” This is very true, and it’s an accomplishment. I also don’t think it would make Zig lose any users, while it would make them aware of this pitfall, which in turn, would make the Zig ecosystem better as a whole. Please do this, or something like it.

                                Side note: do you want me to officially report that compiler bug?

                                1. 5

                                  I would be happy if you and your team said, “Zig has function colors, but the compiler does most of the work.” This is very true, and it’s an accomplishment. I also don’t think it would make Zig lose any users, while it would make them aware of this pitfall, which in turn, would make the Zig ecosystem better as a whole. Please do this, or something like it.

                                  Your definition of function coloring doesn’t match other people’s nor it does help addressing the individual problems that async/await has. You’re conflating syntactical requirements from the language (or lack of there of) with semantic requirements from continuations, and then you made this whole discussion too personal to be able to have a productive exchange, otherwise you would have noticed the irony behind your crusade about lies in a blog post titled “Zig’s Colorblind Async/Await”.

                                  1. 2

                                    There is no irony when your blog post title is wrong.

                              3. 3

                                I am not full of garbage.

                                You might not be, but your program certanly is, because it’s UB.
                                Every suspend needs to be matched by a corresponding resume.

                                1. 3

                                  Then Zig’s documentation example called suspend_no_resume.zig is full of garbage too:

                                  const std = @import("std");
                                  const expect = std.testing.expect;
                                  
                                  var x: i32 = 1;
                                  
                                  test "suspend with no resume" {
                                      var frame = async func();
                                      try expect(x == 2);
                                      _ = frame;
                                  }
                                  
                                  fn func() void {
                                      x += 1;
                                      suspend {}
                                      // This line is never reached because the suspend has no matching resume.
                                      x += 1;
                                  }
                                  

                                  I don’t think it’s a good idea to have UB in your examples unless you specifically say it is UB and to never do it. Even then, I don’t think it’s a good idea.

                                  1. 0

                                    Have you, like, tried reading the next line right after the example?

                                    In the same way that each allocation should have a corresponding free, Each suspend should have a corresponding resume.

                                    1. 6

                                      Yes, that’s there, but it does not mention UB. Leaking memory is not UB, so you’re still misleading the reader.

                                  2. 2

                                    If it isn’t already, perhaps that would be a good feature request for a compiler warning or error.

                                    1. 4

                                      That’s unfortunately impossible, as far as I know.

                                      If you’re using, say, epoll/kevent (which can notify your application when a socket becomes readable or writable), roughly speaking you’re supposed to give epoll/kevent a pointer to your async frame while suspending it. Then, when epoll/kevent sends you a notification, you get the pointer back and resume it. The matching needs to be semantic (every async frame that suspends must be eventually resumed to completion), rather than just syntactical.

                                  3. 2

                                    Side note: it was really painful getting started in Zig. I hate how the compiler won’t let me use tabs, among other things.

                                    Well, I guess I won’t be looking at zig any time soon.

                              4. 3

                                For me, the coloring problem describes both the static and runtime semantics. Does Zig handle the case where a function called with async enters some random syscall or grabs a mutex that blocks for a long time and isn’t explicitly handled by whatever the runtime system is or does that end up blocking the execution of other async tasks?

                                The reason why the runtime semantics matter to me when it comes to concurrency is because if you can block threads, then you implicitly always have a bounded semaphore (your threadpool) that you have to think about at all times or your theoretically correct concurrency algorithm can actually deadlock. That detail is unfortunately leaked.

                                1. 6

                                  If you grab a standard library mutex in evented I/O mode then it interacts with the event loop, suspending the async function rather than e.g. a futex() syscall. The same code works in both contexts:

                                  mutex.lock();
                                  defer mutex.unlock();
                                  

                                  There are no function colors here; it will do the correct thing in evented I/O mode and blocking I/O mode. The person who authored the zig package using a mutex does not have to be aware of the intent of the application code.

                                  This is what the Zig language supports. Let me check the status of this feature in the standard library… looks like it’s implemented for file system reads/writes but it’s still todo for mutexes, sleep, and other kinds of I/O. This is all still quite experimental. If you’re looking for a reason to not use Zig, it’s that - not being stable yet. But you can’t say that Zig has the same async function coloring problem as other languages since it’s doing something radically different.

                                  1. 4

                                    Thanks for the explanation and standard library status information.

                                    I think the ability to make a function async at call time rather than at definition time is the best idea in Go’s concurrency design, and so, bringing something like that to a language with a much smaller runtime and no garbage collector is exciting. I look forward to seeing how this, and all of the other interesting ideas in Zig, comes together.

                                    (p.s. thanks so much for zig cc)

                              5. 6

                                The main argument as perceived by me:

                                Type systems of several languages are already Turing-complete. But, typically, the type system “language” makes expressing logic difficult. It is also a completely separate language so that you cannot e.g. reuse collections.

                                So why not allow the host programming language in type definitions?

                                The article skips over an important consideration: I think the design of a programming language nudges you towards a certain style. If you make complex types complicated, people will use them less.

                                It is hard to predict whether that makes the code easier or harder to understand in general. But I’d love to see experimentation in this field!

                                1. 1

                                  Am I the only one seeing different font sizes in each line of the code examples?

                                  1. 2

                                    I see that too.