← Back to context

Comment by unshavedyak

3 years ago

How is this implemented in practice? Special care to keep io on the outermost layers? Never thought about software in this way. Seems really tough, but interesting

Wonder how well it scales to larger applications. Ie is there a codesize where io-less becomes too difficult? Perhaps performance concerns? Hmm

It's not really an "application" thing. It's meant to be a design for libraries that implement protocols of some sort. All the library API acts on byte buffers and leaves the network socket etc stuff to the library user. So when the library needs to write data to a socket, the API instead returns a byte buffer to the caller, and the caller writes it to the network socket. When the library needs to read data from a socket, it instead expects the caller to do that and then give the populated byte buffer to a library function to ingest it.

Also, quite the opposite, it's *easier* to design a library this way because it's strictly less code the library needs to contain. Specifically in Rust it also has the advantage that the library becomes agnostic to sync vs async I/O since that's handled by the library user. Correspondingly, it is slightly harder for the library user to use such a library, but it's usually just a matter of writing a tiny generic wrapper around the network socket type to connect it to the library functions.

  • Nice from a testing standpoint too, since you can trivially mock out the hardware.

    That said, a lot of what’s actually hard about IO is the error/fault handling, imposing timeouts and backoffs and all that jazz. At a certain point I’d wonder if extracting this out to a separate interface might obscure the execution flow in some of these scenarios.

    • > That said, a lot of what’s actually hard about IO is the error/fault handling, imposing timeouts and backoffs and all that jazz.

      Application-level timeout/backoff handling is always scary to me, because I don't know how to make robust tests for it. I wonder if you couldn't use the same I/O-less approach, and split the logic out into pure functions that take the time passed/error state/... as value arguments, instead of measuring the physical time using OS APIs. It's probably not something for reusable libraries, but it could still be a nice benefit to be able to unit test in detail.

      2 replies →