Live data from Hacker News

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

blog.ivnj.org

31–40 of 83 posts

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

#31

Earlier quoted context omitted.

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 t…

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 has the I/O effect.

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

#32
With Zig I/O you can call i/o functions from non-i/o functions and non-i/o functions from i/o functions.

In the analogy of “What color is your function”, you can call blue functions from red functions and red functions from blue functions.

The pernicious viral nature of function coloring doesn’t apply.

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

#33
post #27

Earlier quoted context omitted.

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.

Huh?

You have the following call stack (top to bottom): a -> b -> c -> d

Function "d" does IO, so a, b and c need to pass that IO down. It's the same as with async in terms of virality.

If I understood correctly, the main benefit here is that you don't have to dance around "can I call this function or is it going to block async runtime reactor?"

I would pick async/await style over this because (I think) extra function argument is higher visual load than "async fn".

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

#34

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.

Go is preemptive. Async/await is cooperative because you’re explicitly cooperating and yield.

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

#35
post #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

This is off topic. But I really hate “services”. They always are just some random collection of functions and aren’t a real object at all.

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

#36
post #33
post #27

Earlier quoted context omitted.

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

Huh? You have the following call stack (top to bottom): a -> b -> c -> d Function "d" does IO, so a, b and c need to pass that IO down. It's the same as with async in terms of virality. If I understood correctly, the main benefit here is that you don't have to dance around "can I call this function or is it going to block async runtime reactor?" I would pick async/await style over this because (I think) extra functio…

  const Greeter = struct {
      io: std.Io,

      pub fn greet(self: Greeter, msg: []const u8) !void {
          var stdout_writer = self.io.stdout.writer();
          try stdout_writer.writeAll(msg);
      }
  }

  Greeter.greet("Look ma, no coloring")

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

#37
post #35
post #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

This is off topic. But I really hate “services”. They always are just some random collection of functions and aren’t a real object at all.

I think I know what you are talking about. In a typical three-tier enterprise application there's no clear separation of concerns between "services" and DAO/Repo... And this is due to how most persistence libraries are designed: they eagerly execute side-effects, this breaks composability due to poor transaction control, so you end up leaking a lot of your business logic into your supposedly persistence layer.

Take a look at Scala's doobie[1]. Any doobie operation returns a `ConnectionIO`, which is only a description of an operation (free monad). With a proper doobie usage the DAO layer is an algebra of possible persistence-related operations, and the service layer implements business logic by combining primitive ConnectionIOs and interpreting them with full control of transaction boundaries.

[1] https://typelevel.org/doobie/index.html

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

#38
post #37
post #35

Earlier quoted context omitted.

This is off topic. But I really hate “services”. They always are just some random collection of functions and aren’t a real object at all.

I think I know what you are talking about. In a typical three-tier enterprise application there's no clear separation of concerns between "services" and DAO/Repo... And this is due to how most persistence libraries are designed: they eagerly execute side-effects, this breaks composability due to poor transaction control, so you end up leaking a lot of your business logic into your supposedly persistence layer. Take a…

I’m sorry but I just don’t enjoy functional Scala.

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

#39
post #36
post #33

Earlier quoted context omitted.

Huh? You have the following call stack (top to bottom): a -> b -> c -> d Function "d" does IO, so a, b and c need to pass that IO down. It's the same as with async in terms of virality. If I understood correctly, the main benefit here is that you don't have to dance around "can I call this function or is it going to block async runtime reactor?" I would pick async/await style over this because (I think) extra functio…

const Greeter = struct { io: std.Io, pub fn greet(self: Greeter, msg: []const u8) !void { var stdout_writer = self.io.stdout.writer(); try stdout_writer.writeAll(msg); } } Greeter.greet("Look ma, no coloring")

`greet` has Io within its closure, so I’m pretty sure this is consistent with the GP’s point.

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

#40
post #38
post #37

Earlier quoted context omitted.

I think I know what you are talking about. In a typical three-tier enterprise application there's no clear separation of concerns between "services" and DAO/Repo... And this is due to how most persistence libraries are designed: they eagerly execute side-effects, this breaks composability due to poor transaction control, so you end up leaking a lot of your business logic into your supposedly persistence layer. Take a…

I’m sorry but I just don’t enjoy functional Scala.

Paraphrasing someone else: then keep enjoying the disfunctional one :-P
Post reply on HN