Hacker News new | past | comments | ask | show | jobs | submit login

Ugh that "Zig has function coloring" post again. The misunderstanding that post makes is that function coloring refers to at compile time. In other words, in Zig you don't need to write two versions of a function to use it in async vs sync context. Obviously the functions will have different runtime representations, so if you do something that cares about runtime calling conventions or w/e then yes you do need to worry about how the function is used, which is unavoidable.



The person you are replying to (me) is the author of that post.

> The misunderstanding that post makes is that function coloring refers to at compile time.

The original function colors post referred to runtime, so you're wrong.

> In other words, in Zig you don't need to write two versions of a function to use it in async vs sync context.

This is correct, if you only care about compile time.

> Obviously the functions will have different runtime representations, so if you do something that cares about runtime calling conventions or w/e then yes you do need to worry about how the function is used, which is unavoidable.

So you admit that Zig does have function colors?

Also, according to the actual Zig documentation, which I quote in my post, there is only one version of every function; if a function is used in an async context, even if it is sync otherwise, the compiler makes the function async and continues on its merry way, so there is only one runtime representation of a function.

Here's the quote from the documentation:

> Zig infers that a function is async when it observes that the function contains a suspension point. Async functions can be called the same as normal functions. A function call of an async function is a suspend point.

Saying that Zig does not have function colors because it hides them at compile time is a little disingenuous, in my opinion. It still has them, but like a statically-typed language with type inference, it just hides that fact, but only at compile time.


> Saying that Zig does not have function colors because it hides them at compile time is a little disingenuous, in my opinion.

Not to impugn your character, but I feel the same way about the points you're making.

> So you admit that Zig does have function colors?

Not in any meaningful way, no. Again, the runtime representation of async vs sync functions is unavoidably different, and has nothing to do with the language. The fact that Zig lets you write one function definition that can be called in sync vs async contexts (unless you're specifically choosing to do sync vs async things) is what it means to be colorless!

> Also, according to the actual Zig documentation, which I quote in my post, there is only one version of every function; if a function is used in an async context, even if it is sync otherwise, the compiler makes the function async and continues on its merry way, so there is only one runtime representation of a function.

I dunno if that's accurate? Someone with more knowledge of Zig internals would have to confirm, but I assume if the same codebase uses a function in each context it'll be monomorphized into the specific version needed, same as generics. I don't think that's contradicted by what you quoted.


It's not accurate that there's only one representation of every function at runtime. Completely unrelated to async, Zig uses compile-time parameters to do stuff similar to templates which will get you lots and lots of monomorphized functions, and you obviously can't at runtime have a pointer to a function that takes a compile-time argument (just like you can't have a pointer to vector<T>::push, to which you would pass the type T at runtime, then a this pointer which is a vector<T>, then a T t to append, haha). Then, for async specifically, usually the way that the same function can be compiled to be async or non-async is for it to be generic over some other stuff at compile time and for that other stuff to either be async or not be async. For example, you could have a function that writes a websocket frame to a writer, and that function is async if the writer is async, but it's not async otherwise. But it's also specialized to each writer in other ways, so if you use it on a lot of types of writers, you'll get more than just 2 copies (one async and one non-async) of it in your compiled program.

In most programs you end up with all async functions or all non-async functions in a specific context, so if someone handed you a pointer you'd have no problem knowing which calling convention to use. But if you actually end up not knowing, because you really do have a mix of different types of pointers, you could call all of the pointers via the @asyncCall builtin. I have done this successfully, but I'm not actually sure whether it worked by having a pointer to a non-async function at runtime or by specializing the function that I was using that did not need to be async (because its body and callees did not use await or suspend) to use the async calling convention. Or you could use a tagged union of multiple function types and make your own `call` method for that union. This is pretty weird though, because you'll end up requiring the end user to provide storage for the async frame even when the function isn't async and doesn't actually need that storage.


> I dunno if that's accurate?

It's not, your understanding of async in Zig is sound. I would even add that in the blog post in question (my post, the one ghoward is replying to in his) I don't even dare to describe Zig as colorless, in fact I say colorblind.


> It's not, your understanding of async in Zig is sound.

Then perhaps there is something wrong with the language reference?

> I don't even dare to describe Zig as colorless, in fact I say colorblind.

Doesn't that mean my blog post is right?

But my biggest beef is that people, including GP, used your blog post to then claim Zig is colorless. I mean, GP did it in GP.


I don't really understand your crusade. I made this same observation in the past, it never satisfied you.

Your blog post is full of wrong information. I tried to explain to you what was wrong when you first posted it (so you can refer to those comments, if you want), but you keep seeing this as some kind of philosophical debate, and I have no interest in having this debate.

As I said to you already in the past, I just write software with Zig async and it works. Up to you what you want to do with your free time.


> I don't really understand your crusade.

Accuracy is important in the marketplace of ideas, and especially in programming. Software is too buggy already, and it would only add more bugs to have programmers not understand the languages they use.

> I made this same observation in the past, it never satisfied you.

Yes, you made that same observation, and I appreciate that. But as @kbd so unintentionally demonstrated, people still believe that Zig is colorless. I want to dispel that notion completely.

I think you are not adding to the problem, and that is great. But the notion is still there.

> Your blog post is full of wrong information. I tried to explain to you what was wrong when you first posted it (so you can refer to those comments, if you want), but you keep seeing this as some kind of philosophical debate, and I have no interest in having this debate.

Here is all of the comments you made on Hacker News on the comments [1] about my blog post.

> 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.

and

> That's how it works in Zig. Calling an async function like this will also await it.

The closest thing to "explain[ing] to [me] what was wrong when [I] first posted it" is probably that first comment, which was in reply to

> I may be totally wrong with this assumption, but the way I understoo[d] Zig's color-less async support is that the compiler either creates a "red" or "blue" function body from the same source code based on how the function is called (so on the language level, function coloring doesn't matter, but it does in compiler output).

> The compiler still needs to stamp out colored function bodies because the generated code for a function with async support needs to look different - the compiler needs to turn the code into a state machine instead of a simple sequence).

> It's a bit unfortunate that red and blue functions appear to have a different "ABI signature", but I guess that's needed to pass an additional context pointer into a function with async support (which would otherwise be the implicit stack pointer).

(Original comment at [2] by flohofwoe.)

So if anybody explained anything, it's flohofwoe.

But flohofwoe's comment goes directly against the the language reference, so it's hard for me to believe.

The language reference says that sync functions are turned async if they call async functions. This implies virality of async on functions, which implies that many functions are definitely async-only.

If the compiler does something different, which it would have to if it actually makes two different versions of each function, then the language reference is wrong. Like I said, accuracy matters, so I would also like to see changes in the Zig language reference about this if that's the case.

> As I said to you already in the past, I just write software with Zig async and it works.

Yes, you write working software in Zig async, but you understand it better than most. People who go to the language reference and write based on that may not be able to write working software with Zig async as easily as you.

[1]: https://news.ycombinator.com/item?id=30965805

[2]: https://news.ycombinator.com/item?id=30967070


> The language reference says that sync functions are turned async if they call async functions. This implies virality of async on functions, which implies that many functions are definitely async-only.

> If the compiler does something different, which it would have to if it actually makes two different versions of each function, then the language reference is wrong. Like I said, accuracy matters, so I would also like to see changes in the Zig language reference about this if that's the case.

To be clear, do you expect this function

  fn invokeFoo(a: anytype, b: anytype) @TypeOf(a) {
    return a.foo(b);
  }
to have only one representation at runtime, and for that one representation to be either async or not async?


That's a generic function. That's not what I'm talking about.


Thanks for confirming. So what does Zig actually do to execute async functions? Green threads in the background? An event loop? Is there documentation on the implementation?


When you set `const io_mode = .evented;` the stdlib observes that value and

- flips I/O functions to evented mode, both by setting the correct flags when creating, say, a socket, and also by having `suspend` and similar keywords in the winning branch of some comptime conditional statements, causing the functions in question to become async also in the language.

- instead of directly calling into main at program start, it starts an event loop and schedules main on it

This is all userland stuff based on a convention, none of this is ingrained in the programming language itself and it could very well be that in the future things will have to be done in a more explicit way.

Async functions are single frames (as opposed to being green threads).


> Not to impugn your character, but I feel the same way about the points you're making.

Again, the original post about function colors talks about runtime, so how could I be disingenuous about that?

> Not in any meaningful way, no. Again, the runtime representation of async vs sync functions is unavoidably different, and has nothing to do with the language. The fact that Zig lets you write one function definition that can be called in sync vs async contexts (unless you're specifically choosing to do sync vs async things) is what it means to be colorless!

I mean, this would be true if it were true. But I tried to use the "blue" function in my examples in both contexts, and it didn't work. In fact, it didn't even work to use the "red" function in both contexts.

> I dunno if that's accurate? Someone with more knowledge of Zig internals would have to confirm, but I assume if the same codebase uses a function in each context it'll be monomorphized into the specific version needed, same as generics. I don't think that's contradicted by what you quoted.

I worded this completely wrong, so let me fix that. I should have said,

> if an async function is used in a sync context, the compiler makes the context async and continues on its merry way, so there is only one runtime representation of the function that was the context.

But it means that the sync function that was the caller is made async no matter what because it calls an async function. So there is no monomorphization; there cannot be since there is a call to an async function that always makes an async context that always turns the caller async.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: