Hacker News new | ask | show | jobs
by valenterry 1331 days ago
> In, say, the Scala ecosystem... limited scope isn't so brilliant. Sometimes you just can't do limited scope.

As in Scala the programming language?

I find it is exactly the opposite. Scala is complicated and hard to learn, but if it can do anything well then it is allowing tools/libraries to be well-scoped and composed by the user.

To give an example: In Scala there are 3 libraries: - One for http requests - One for async calls - One for json (de)serialization

Those libraries don't know anything of each other and don't use the same interfaces/types, so by default you have to do some plumbing to make them work together.

But there are 2 more libraries: - One that provides integration of http and json - One the provides integration of http and async

As an end user that wants to build an async web app that uses json, you just have to add those 5 libraries and 5 imports and then everything just magically works out of the box. And it compile-time safe. No reflection or magic things happening. If anything doesn't work out, for example due to version differences, then it just doesn't compile and you adjust the plumbing yourself or update the libraries.

I seriously don't know any even half popular language that supports that. Take Rust for example. Rust has traits and allows you to make whatever types you have become compatible. But you can't write libraries on top of existing ones to do the plumbing for the user who then just picks the plumbing they like.

1 comments

Which of these libraries does I/O, and why is it any of them?

In Zig, I am using a TLS library that does not perform I/O, it just writes to writers and reads from readers, so using it with io_uring or DPDK or send/recv or pcap files requires 0 additional work.

Depends on what you mean by IO. The http library can be used without IO (i.e. you give it a request and it gives you the response and potentially does some effect (but that's e.g. done through the async library). But it also includes functionality to bind to a port and listen to requests there in the way the async library (or maybe a streaming library) controls etc.

> In Zig, I am using a TLS library that does not perform I/O, it just writes to writers and reads from readers, so using it with io_uring or DPDK or send/recv or pcap files requires 0 additional work.

Of course. Now imagine you need backpressure (reactive streaming) and writer/reader interfaces don't support that. Can someone provide you a library that you import and then automatically the types of the TLS library change and it works with the new interfaces that it doesn't even know exist? Because that's what I'm talking about.

I/O usually means syscalls, I guess. If I install a library to encode and decode a protocol and it has no mode of operation that performs 0 syscalls, it is defective.

I'm not sure how "write does not even return until the writing is done" can avoid handling backpressure.

Unfortunately, I have had to add calls to flush() to the TLS library because it was not made with buffered writers in mind. I guess instead of writing flush() a couple of times I could have written a library that wraps a pair of buffered writer and reader in a new reader which flushes the writer if necessary when you read it and wraps the TLS library so that the passed-in reader and writer are automatically wrapped by that new reader. This sounds like a bit much though, since the whole task was to call flush a couple times only during TLS handshakes only if flush was defined.

> I/O usually means syscalls, I guess. If I install a library to encode and decode a protocol and it has no mode of operation that performs 0 syscalls, it is defective.

I agree, but that wasn't really the point that I was trying to make. Even without IO, there can still be different interfaces, such as specific interfaces for errors, attaching meta information like tracing, logging etc.

I don't know how ZIG solves that, but in Java this is a PITA because the language isn't capable enough - so everyone falls back to global runtime configurations, e.g. some file being somewhere that configures it. It doesn't compose and is super hard to debug. And that's just an example.

> I'm not sure how "write does not even return until the writing is done" can avoid handling backpressure.

That doesn't matter. The point is that you are using the reader/writer interface. So if you want to plug this library into another library that works with different interfaces then you now have to convert stuff around by hand all the time.

You could say that everyone should just use "standard" interfaces, but often they don't exist or lack certain properties or it's simply hard to standardize everything. Think about how many libraries for dates/times there are in Java or python.

EDIT: I can see that it might be a bit hard to wrap your head around what I mean because as developers (including myself) we learn to do plumbing all the time and it feels like you just have to do it in almost any language I always used.

What sorts of features do you mean? I explained how I would achieve the desired thing. It's been a good decade since I used Scala.
Sorry, I'm not sure what you are asking me to explain - I didn't use the term features at all. Can you elaborate?