- regarding the bulk of these patterns, which are all just different encodings of error values:<p>- the primary value prop of error values is they are concurrent, i.e. you can map over a collection with an effectful fn and end up with a collection of maybe errors (where error is encoded as nil, map, Either, whatever)<p>- exceptions cannot do this<p>- furthermore, clojure’s default collection operators (mapcat etc) are lazy, which means exceptions can teleport out of their natural call stack, which can be very confusing<p>- error values defend this<p>- the problem is that now you have a function coloring problem: most functions throw, but some functions return some error encoding<p>- this additional structure is difficult to balance, you’re now playing type tetris without a type system. Clojure works best when you can write short, simple code and fall into the pit of success. Type tetris is pretty much not allowed, it doesn’t scale, you’ll regret it<p>- you’ll also find yourself with a red function deep in your logic that is called by a blue function, at which point you’ll find your self doing the log-and-discard anti pattern<p>- therefore, i agree with the first bullet: it’s a hosted language, host exceptions are idiomatic, don’t over complicate it<p>- i do think error values can work great locally, for example (group-by (comp some ex-message) (map #(try-ok (f! %))), here i am using ex-message as a predicate. the point is you need to gather and rethrow asap to rejoin the language-native error semantics so your functions are no longer colored<p>- i am not an authority on this, just my experience having explored this a bit, wrote a big system once using a monadic error value encoding in clojure (using the funcool either type) and was very unhappy. The minute you see >>= in a clojure codebase, it’s over. (mlet is ok locally)<p>- one thing building Electric Clojure taught me, is that the language/runtime can encode exception semantics “however” and still expose them to the user as try/catch. Which means we can deliver the value prop of error values under the syntax of try/catch.<p>- That means, interestingly, Electric v2’s exceptions are <i>concurrent</i> - which means an electric for loop can throw many exceptions at the same time, and if some of them resolve those branches can resume while the others stay parked.<p>- For Electric v3 we have not decided if we will implement try/catch yet, because Electric userland code is essentially “pure” (given that IO is managed by the runtime and resource effects are managed by an effect system). Userland doesn’t throw, platform interop (database txns) is what throws, and we’ve found only very minor use cases for needing to catch that from Electric programs, again due to their purity. Having network failure not be your problem is really great for code complexity and abstraction!