Clojure, like node.js, is a very opinionated platform. The funny thing is that almost every opinion is different.
Clojure embraces Java as a platform.
- Originally, every declared identifier was overrideable on a per-thread basis.
- There’s many features (e.g. Futures and Reducers) that allow you embrace multi-threading at a high level.
- Data is immutable.
- Data is globally shared between threads.
- It adds STM to Java’s already extensive thread-synchronization primitives.
- Everything’s a function.
- It’s aggressively single thread and asynchronous.
- If you want another thread, you’ll have to start another process.
- Everything’s mutable, even class definitions.
- Share data between processes? I hope you like memory mapping.
- Synchronization barriers? You don’t need them.
- Everything’s an event with a callback.
Clojure and Node have completely different sweet spots: clojure is truly excellent at computation, node at IO. Like it or not, multiple threads aren’t really a good solution to blocking IO solutions. Which is a pity, because all the main Clojure libraries feature blocking IO (e.g. clojure.java.jdbc, ring). That’s not to say there isn’t some amazing stuff being done in Clojure, just that it could be even better.
JDBC is an interesting case because it’s a Java problem that works its way through to Clojure. Node.js made a virtue of being the only API on a new platform. However, it introduces a couple of oddities of its own. For instance, the jdbc library can only have one open database connection at once. Usually the case, but sometimes undesirable (try performing a reconciliation of a million records between two databases). To some extent, this is a hangover of Clojure being envisaged as an application language that used libraries written in Java.
There’s nothing stopping you from writing Clojure code in a node-like style, as long as you’re prepared to write your own web-server (Webbit, Aleph) and DB libraries (er… no-one). Equally, implementing a feature like co-routines wouldn’t actually be that hard, but you’d lose bindings, which is a problem for any library that assumes that they work. And you’d still need all of your libraries to be async.
For all these reasons, I don’t think we’re going to be seeing a proper Clojure async solution any time soon. Ironically, I think it’s the complete absence of async DB libraries that is really holding it back. Without that, solving most of the other things isn’t really that useful.