clojure 2026-06-27

What has been the most clojure idiomatic way of approaching i/o that you have seen? I don't mean "hey this works with java's cumbersome api in some way". I mean what really feels and looks like "hey, this feels really natural for clojure as a language".

The itch you are feeling about IO being an annoying special case in the land of functional programming is correct. Many others have felt it before you. The bad news is that IO is “effectful” and cannot be made functional in the general case, so every language community that prefers the functional style converges on the same approach: encapsulate IO effects and hope for the best.

👍 1

what do you mean by io exactly? is a real workhorse that feels pretty natural

slurp and spit 😎

💯 1

if you're looking for file system operations: https://github.com/babashka/fs

i meant the style of handling input/output operations, like reading from a file or writing to a file ... slurp and spit are simple, but only for a specific scope, that doesn't scale well to immense files or network socket streams (let alone udp "streams") or i/o with a launched sub process from the operating system.

imho IO is intently procedural/imperative so my go-to approach in any functional setting is to have an imperative shell and a functional core It works by push side effects as much as possible to the boundaries of the code (the imperative shell) and transforming/marshalling data across this boundary, this allow for idiomatic code in the functional core which is blissfully unaware of IO Few concrete examples: in FRP sources and sinks are the shell, the rest is the core; in Haskell the interact function abstracts the shell; clojure transducers (https://clojure.org/reference/transducers explains this decoupling); etc This approach is ubiquitous nowadays and almost all middleware/interceptor based web framework is doing this for the request IO (although usually handlers are still doing IO for database)

> How can you possibly ensure the same things when IO demands mutability

at least for read only operations on a file that hasn't changed, i could just remember "hey this pseudo pointer here was this file at this offset" ... repeated accesses to the same thing would give the same result, unless someone intentionally goes and changes the file.

> repeated accesses to the same thing would give the same result, unless someone intentionally goes and changes the file Or the underlying filesystem experiences some issue, including network issues for NFS and similar things. But if you don't care about those and about changes from outside the app, line-seq is, I think, the only thing that's just a single function and that doesn't require interop. (Although arguably "feels natural for Clojure" includes relying on interop.) If you don't care about lines or text, specific approaches depend on what exactly you need. "A file with random bytes", "a file backing a columnar store", "a socket", etc. - all different use-cases, all require different approaches.

I/O is challenging because: • side effects • non idempotent operations • non commutative operations • often asynchronous • operations take varying amounts of time • require resource management • error handling. almost any operation can throw an exception As an approach, I've found using core.async.flow to be very helpful. Another approach I've seen in other functional languages that I haven't seen tried in clojure yet is algebraic effects à la https://koka-lang.github.io/koka/doc/book.html and https://ocaml.org/manual/5.5/effects.html.