This is effectively a special calling convention: Zig expects you to pass in a "token" object that communicates a kind of effect (I/O in this case). No token, no effect (modulo a soundness hole).
This is not a new pattern, and I think it's a pretty good one (and is arguably more ergonomic and general than syntax-level effects). But it's quintessential function coloring.
It does no such thing. you could pass a function a vtable and the vtable could have one implementation that calls an io stashed in the parent of the vtable, and a different vtable that doesnt and the function calling the vtable would be none the wiser. what is the color of the function that took the vtable?
this is not just academic; it would be for example the basis for mocked integration tests on a database or over the net api call.
That's a calling convention, with indirection. You need some kind of capability token for this kind of asynchronicity scheme; it doesn't matter how you get it, but it needs to be there.
To be clear, there's nothing wrong with this; it's just another way to encode capabilities/effects.
> what is the color of the function that took the vtable?
It also has the I/O effect. You don’t need to call or make use of an effect to be “tainted” by it; it just needs to be in the closure (or whatever scope is relevant in the language).
Intuitively: if you mark a function as async, it doesn’t stop being async “colored” just because you don’t actually perform any async operations in it. This is the same thing.
you might be talking about stackless coroutines, which are currently not part of zig, in which case, yes, the compiler might [0] have to instantiate different functions under the hood for the stackless-coro and the non-stackless-coro cases, with some mechanism to drop in an executor at the boundaries. until then its really hard to claim that the functions are colored.
[0] But even in that case there's not really coloring because if you provide a single io implementation there won't be different functions, even at the compiled level.
No, I really just mean from a PLT perspective. The sync/async implementation used under the hood doesn't matter; what matters is that an `Io` in the closure is a token type for communicating effects. No instantiated or enclosed token; no effects.
People seem to be really defensive about this, like it's a bad thing. It isn't! It's arguably a significantly cleaner way to handle what people confusingly call "coloring." But that doesn't make it not "coloring," because coloring is about effects and vitality, not about keywords and syntax.
1. the original author of the function coloring post wad not exactly a theorist so if you're applying some other idea of what coloring is, then you're muddying the waters.
2. here's what i have to say about your idea of what coloring is: That's all fine and good in theory, but in practice it makes no difference, at the user level, or at the compiler level.
I don’t know anything about the original author, but I do know what function coloring is. It’s a way to describe effects. An effect is a function color.
> That's all fine and good in theory, but in practice it makes no difference, at the user level, or at the compiler level
Every example given so far shows Io’s virality, so I don’t know how you can assert how it doesn’t make a difference. The entire point of the design appears (reasonably) to be to introduce a token object that conveys an effect, rather than requiring a runtime to intermediate that effect.
Again, none of this is bad. Effect typing is cool. But it is, by definition, a way to color a function.
(Maybe the confusion here stems from the fact that languages like Go appear to have it “both ways” without coloring. Go is able to do that because it has an intrusive runtime that intermediates asynchronous events. Zig can’t do the same thing without making the same compromises as Go vis a vis FFI performance and ABI compatibility.)
I think I almost agree with what I think you mean, but I'm not sure, hopefully you'll entertain a few questions?
Say you have two functions one that does some kind of IO, (say a pair of bidirectional read, write, from stdin and stdout) and another that returns a block of memory of a constant size. Would you say that one is colored, and the other isn't?
I'll also try to make an attempt at the idea I'm trying to figure out too. I genuinely can't predict which side you'll answer for, but I'm assuming you'll say that it's not colored, because while it does do IO, but only directly though the global file descriptors which have no baring on the calling conventions. Which means the decisions and impact about which color this IO function doesn't ever apply to the callers and callees. This I think is the virality you mean where the callers and callees are required to know [something] about the semantics? I probably agree you could call this coloring and defend it... It's interesting that this might be closer to the "accepted" definitions of what any language means when it says this function is colored. But I don't think this understanding most people have about the concept of coloring. Most I see are arguing about the semantic effects, much more than the "accepted" definition. I have the distinct impression that when most say coloring, the most significant implication is when and where you resolve the specific color. Will it be sync or async code, when, where, and who gets to decide.
Or perhaps I'm wrong, and you would say that one function is colored, but the other isnt? Then I'd ask if you can tell it's colored, can you tell me which color?
In both cases I'd argue it's better to embrace the semantic widening of the concept of function coloring, because what I understand from how you describe it, I feel that saying this is coloring is strictly less enlightening about the reality of the code and system together, than calling this function alpha. With zig's new IO interface, I can't tell you what the RGB values are for the color, but I can confidently say, it's alpha channel is 0xff. You might not be able to tell me what color it is, but it without a doubt isn't a pure function, interacts with IO and does have a null byte in the alpha channel.
im no virologist but i don't think your definition of viral is reasonable. this is like saying "anything that wants to use a database needs a socket, so a socket is viral"
This is not a new pattern, and I think it's a pretty good one (and is arguably more ergonomic and general than syntax-level effects). But it's quintessential function coloring.