Live data from Hacker News

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

blog.ivnj.org

11–20 of 83 posts

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

#11

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

Rust calling async function in non-async function:

    ...
    // Create the runtime
    let rt  = Runtime::new().unwrap();
    
    // Get a handle from this runtime
    let handle = rt.handle();

    // Execute the future, blocking the current thread until completion
    handle.block_on(async {
        println!("hello");
    });
https://docs.rs/tokio/latest/tokio/runtime/struct.Handle.htm...

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

#12
Function colouring created a lot of angst when it first came about, particularly because of the difficulties of calling a function of one colour from another. Whether that was possible, what it actually meant, wasn't really well defined.

As other comments have said, there's nothing special about "colouring"; sync/async functions are a case where those above problems are tough, but simpler versions of the problem are everywhere and we don't freak out about them e.g. call a fallible function from an infallible function.

It really all turns on how easy it is to ultimately make the call to the other "function" colour. In Zig's case, if its easy to get an Io in a function that didn't take an Io, it's a non-issue. Likewise for the "fallible function call from infallible function": if it fails, do something that doesn't result in the infallible function failing (do something else? Terminate? Anything will do).

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

#13
I am actually really excited about this.

The issues I've had with function colouring had to do with trying to compose code using (or expecting) blocking effects with those using async effects in NodeJS - if one library has a higher-order function that expects a non-async function and you have functionality which is provided to you as async, it can be very difficult to plumb them together! And if it's the other way around, it can be quite the performance killer (think how much faster better-sqlite3 is than alternatives). Zig's approach eliminates this problem, AFAICT.

If I had to choose between having to pass through an effect handler like `io` or write `async` everywere, the former seems like a better use of my time. It's explicit, but that can be good.

It also fits Zig well with the allocator. Code can expect an allocator or perhaps an allocator and `io`, or perhaps neither. It's analogous to Rust code that is core vs alloc/nostd vs std.

I am slightly amused that a "C-but-better" language is going to have an `io` passed through non-pure functions much like Haskell. It's that idea combined with Rust's pluggable async runtimes (and stackless concurrency) combined with Roc's "platforms" - but for systems programmers. Quite amazing.

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

#15
post #11

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 Rust calling async function in non-async function: ... // Create the runtime let rt = Runtime::new().unwrap(); // Get a handle from this runtime let handle = rt.handle(); // Execute the future, blocking the current thread until completion handle.block_on(async { println!("hello"); }); https://do…

Of course, spinning up a new runtime within the context of a boundary like that is probably wasteful (lots of new threads created if you’re not careful). But you could stash that runtime behind a OnceLock (you’d need to block_on the Handle I imagine rather than the Runtime directly, but doable).

And calling blocking from non-blocking:

    let result = tokio::task::spawn_blocking(|| {
       5
    }).await;
This of course is basically essentially what Zig is doing, except instead of hidden global state it’s parameter passed. This is one area Zig does do better in - I wish Rust would default more to instance state instead of implicit global state.

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

#17
post #7

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…

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.

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

#18
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…

> Then we have to start talking about function colouring in a whole bunch of new contexts like with Zigs explicit passing of allocator.

That's pretty much where we are though. If you have a function that isn't passed an allocator, and now it needs to call a function that does take an allocator, we're in the same place.

Rust's 'async' keyword changes the type of the return value, but you can just write the different return value yourself; it's 'coloured' purely by what it means to be returning a 'Future'.

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

#19
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…

> Then we have to start talking about function colouring in a whole bunch of new contexts like with Zigs explicit passing of allocator. That's pretty much where we are though. If you have a function that isn't passed an allocator, and now it needs to call a function that does take an allocator, we're in the same place. Rust's 'async' keyword changes the type of the return value, but you can just write the different r…

So its impossible to get an allocator through a different means from inside a function? It must be passed in?

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

#20
post #19

Earlier quoted context omitted.

> Then we have to start talking about function colouring in a whole bunch of new contexts like with Zigs explicit passing of allocator. That's pretty much where we are though. If you have a function that isn't passed an allocator, and now it needs to call a function that does take an allocator, we're in the same place. Rust's 'async' keyword changes the type of the return value, but you can just write the different r…

So its impossible to get an allocator through a different means from inside a function? It must be passed in?

No, it's entirely possible. And that's why we don't think about "has an allocator" as a colouring problem.

Likewise if JavaScript had an easy way to get a handle to its Runtime, and a function "block on promise" in its early days, we'd have never had all these "colouring" arguments.

Post reply on HN