Live data from Hacker News

Zig's new I/O: function coloring is inevitable?

blog.ivnj.org

21–30 of 83 posts

Re: Zig's new I/O: function coloring is inevitable?

#21
post #2

I really don’t agree with the idea that this is functional colouring. Then we have to start talking about function colouring in a whole bunch of new contexts like with Zigs explicit passing of allocator. Or any other parameter that needs to be explicitly passed to use some kind of interface. I think we should stick to talking about colouring when there is special calling conventions or syntax, which has the consequen…

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.

Re: Zig's new I/O: function coloring is inevitable?

#22

If you want to go down that route, any function that has, or doesn't have, any given resource is colored then. fn foo(db: *Db) !void { ... } fn bar() !void { ... } Would you consider `foo` a blue function and `bar` a red function? That doesn't seem particularly helpful to me. The virality of async await is that once you mark a function async, then you can only call it from another async function, which forces you to…

> Would you consider `foo` a blue function and `bar` a red function? That doesn't seem particularly helpful to me. In the sense of effect/capability typing, I think the answer is yes. "Coloring" isn't magical, it's just a way to describe effects. Those effect can be described by keywords (`async` in JS, `throws` in Java, etc.) or special token parameters/types (what Zig does), but the consequences are the same: the e…

Yeah, parameter passing can be seen as some kind of effect (like the Reader monad in Haskell). Passing parameters down the call stack is viral just like function coloring (if you need to do i/o and thus need to receive an io parameter, then your caller must receive an io parameter too, recursively; this is analogous to adding a keyword async in front of your function, and to its callers recursively). The solution is probably some sort of implicit parameter, again, like the Reader monad.

Note: async fn in Rust is also just a fn (in Rust's case, a fn that returns a future). It turns out that returning a future, or receiving as parameter a reference to the runtime, are equivalent in some sense

Re: Zig's new I/O: function coloring is inevitable?

#23
post #7

Earlier quoted context omitted.

I've been trying to beat this point in and failing. If a parameter type creates "colors", you can extrapolate that to an infinite set of colors in every single language and every single standard library, and the discussion on colors becomes meaningless. Some people are so focused on categorical thinking that they are missing the forest for the trees. The colors are a means of describing an observed outcome -- in Node…

> you can extrapolate that to an infinite set of colors in every single language and every single standard library, and the discussion on colors becomes meaningless. It's more that discussion about most of them becomes meaningless, because they're trivial. We only care when it's hard to swap between "colours", so e.g. making it easy to call an Io function from a non-Io function "removes" the colouring problem.

> so e.g. making it easy to call an Io function from a non-Io function "removes" the colouring problem.

Exactly. In golang (which is also a cooperatively multithreaded runtime if I understand correctly), calling a function that needs IO does not infect the callers type signature.

In async rust, and in "async param" zig, it does.

Re: Zig's new I/O: function coloring is inevitable?

#24

If you want to go down that route, any function that has, or doesn't have, any given resource is colored then. fn foo(db: *Db) !void { ... } fn bar() !void { ... } Would you consider `foo` a blue function and `bar` a red function? That doesn't seem particularly helpful to me. The virality of async await is that once you mark a function async, then you can only call it from another async function, which forces you to…

Well said. Honestly I don't understand this "function coloring argument" at all in zig's context.

Function is the smallest unit of logic. And function parameter is the fundamental way to control it.so, in theory, __A function parameter is the smallest possible design choice you have to control async vs sync__

There is no way to reduce it further. As you need to encode this somehow, otherwise it will be implicit and very hard to debug.

functions can compose. Function parameters can be composed. Making this a solid design choice.

Zig has somehow achieved this feat of decomposing async/sync to a single parameter. This deserves an ovation Other langs should take note.

Re: Zig's new I/O: function coloring is inevitable?

#25
post #2

I really don’t agree with the idea that this is functional colouring. Then we have to start talking about function colouring in a whole bunch of new contexts like with Zigs explicit passing of allocator. Or any other parameter that needs to be explicitly passed to use some kind of interface. I think we should stick to talking about colouring when there is special calling conventions or syntax, which has the consequen…

> which has the consequence of having to write separate libraries/modules for async code and non-async code.

That is not a consequence of function coloring or syntax, it is a consequence of having multiple ways of performing IO.

Re: Zig's new I/O: function coloring is inevitable?

#26
If we apply your logic, then every mainstream language has the coloring problem with not just two, but a multitude of colors.

  // OMG we can't call this without passing the service
  // This function is people-colored
  public Person findPersonByName(PeopleService service, String name) {
    // OMG we can't find without the service
    service.find(name)
  }


EDIT: formatting

Re: Zig's new I/O: function coloring is inevitable?

#27

Earlier quoted context omitted.

> Would you consider `foo` a blue function and `bar` a red function? That doesn't seem particularly helpful to me. In the sense of effect/capability typing, I think the answer is yes. "Coloring" isn't magical, it's just a way to describe effects. Those effect can be described by keywords (`async` in JS, `throws` in Java, etc.) or special token parameters/types (what Zig does), but the consequences are the same: the e…

Yeah, parameter passing can be seen as some kind of effect (like the Reader monad in Haskell). Passing parameters down the call stack is viral just like function coloring (if you need to do i/o and thus need to receive an io parameter, then your caller must receive an io parameter too, recursively; this is analogous to adding a keyword async in front of your function, and to its callers recursively). The solution is…

> Passing parameters down the call stack is viral

It's in a different league comparing to async-await abomination. For one, you can store parameter on a struct thus working around the "virality" on a call site.

Re: Zig's new I/O: function coloring is inevitable?

#28
post #2

I really don’t agree with the idea that this is functional colouring. Then we have to start talking about function colouring in a whole bunch of new contexts like with Zigs explicit passing of allocator. Or any other parameter that needs to be explicitly passed to use some kind of interface. I think we should stick to talking about colouring when there is special calling conventions or syntax, which has the consequen…

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.

> Zig expects you to pass

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.

Re: Zig's new I/O: function coloring is inevitable?

#29

If you want to go down that route, any function that has, or doesn't have, any given resource is colored then. fn foo(db: *Db) !void { ... } fn bar() !void { ... } Would you consider `foo` a blue function and `bar` a red function? That doesn't seem particularly helpful to me. The virality of async await is that once you mark a function async, then you can only call it from another async function, which forces you to…

> The virality of async await is that once you mark a function async, then you can only call it from another async function

That's not exactly true. Many languages return a curried version of it, which can be executed once you have a runtime.

Re: Zig's new I/O: function coloring is inevitable?

#30

Earlier quoted context omitted.

> you can extrapolate that to an infinite set of colors in every single language and every single standard library, and the discussion on colors becomes meaningless. It's more that discussion about most of them becomes meaningless, because they're trivial. We only care when it's hard to swap between "colours", so e.g. making it easy to call an Io function from a non-Io function "removes" the colouring problem.

> so e.g. making it easy to call an Io function from a non-Io function "removes" the colouring problem. Exactly. In golang (which is also a cooperatively multithreaded runtime if I understand correctly), calling a function that needs IO does not infect the callers type signature. In async rust, and in "async param" zig, it does.

Another poster up thread identified the exact problem: async/await contexts are not first-class values, they are second class citizens. If they were values then you could just stick the context in a struct/class and pass that around instead, and avoid having to refactor call chains every time something changes. It's their second class status that forces the "colouring" into the function signature itself at each point. This is also why ordinary first class values do not introduce colours, ie. you can hide new values/parameters inside other types that are already part of the function signature, thus halting the propagation/vitality of the change.

Of course, if these async contexts were first class citizens then you've basically just reinvented delimited continuations, and that introduces complications that compiler writers want to avoid, which is why async/await are second citizens.

Post reply on HN