Hacker News new | ask | show | jobs
by midnight_eclair 39 days ago
fun fact: clojurescript had support for asynchronous paradigm through core.async library (CSP style) long before async/await landed in javascript itself.

edit: i'm in no way trying to diminish the value of this release, just pointing out how cool it is that you can get new language features before they are available in the host language by just adding a library to your dependencies. clojure is awesome!

2 comments

> fun fact: clojurescript had support for asynchronous paradigm through core.async library (CSP style) long before async/await landed in javascript itself.

Definitely. I was heavily using it and it worked: a few quirks but we did have async/await since more than a decade. I think I discovered it after watching a talk by David Nolen.

Since then I moved to minimal JavaScript on the front-end: SSE is one-way and that is beautiful. I'm glad to see many devs, from a great many different languages, now getting interested in SSE.

Here's a great, recent, talk by David Nolen called "A ClojureScript Survival Kit":

https://youtu.be/BeE00vGC36E

I cannot thanks David "Swannodette" Nolen enough for all the work he did on ClojureScript (and core.async) since its inception. And what's amazing in this talk is that he's actually excited at the idea that we may do away with ClojureScript and use pure Clojure (on the server-side) and server-side events, with just a tiny of JavaScript.

The real demo starts around 26:30. He shows a Webapp running on the client and how much resources it's using, then he shows the exact same Webapp running on the server and pushed one-way to the client using SSE. It is wild: resources usage drops to near zero.

YMMV but I find it easier to reason about my webapps and manage state now that I'm using a minimal DOM morphing lib: I used to have two REPLs (one for Clojure, one for ClojureScript) and lots of back-and-forth traffic and hard-to-track / reproduce state. Now everything is definitely snappier and way easier to reproduce.

I'm not saying SSE is going to work in every case though: YMMV. But in any case the video or at the very least the demo starting @ 26:30 is very much worth watching.

thanks for the link, i was just recently trying to find that talk!

re server-side rendered fragments - htmx is extremely easy to integrate with your clojure(script) projects, i found it to be quite pleasant paradigm, but i have no skin in the game so take it with grain of salt xD

True, but there are many reasons to avoid core.async, especially in 2026.

It balloons up the Js artifact, has no inherent error model, and transforms into state machine code that's hard to read/debug if something goes wrong. Plus, the `go` macro encourages overly-large functions, because it can't transform code outside its own sexpr.

As one Cognitect put it, "core.async is beautiful nonsense".

> has no inherent error model

I'll pitch in here, as I've been doing a lot of thinking about this issue and ended up writing my own (tiny) tools for handling anomalies, modeled on the very well thought-out https://github.com/cognitect-labs/anomalies categorization.

This is actually a much wider problem and not specific to core.async. Handling anomalies is difficult. It used to be that you would have exceptions and errors which would be thrown, unwinding the stack. This pattern no longer works in asynchronous code, or code that needs to pass anomalies between the server and the client. In practical applications, an anomaly might need to be returned from a function, passed through a `core.async` channel, then thrown, unwinding the stack on the server side, then caught and passed to the client side over a WebSocket, and then displayed to the user there.

Solving this well is not easy. I think my toolkit, iterated and improved over the years, is close to what I need. But I'm pretty sure it wouldn't handle all the real-world use cases yet.

But again, this is not specific to core.async in any way.

What is your opinion on farolero[0]?

[0]: https://github.com/IGJoshua/farolero

At a first glance, it does much more than what I would want to.

My status toolkit just extends the Cognitect anomalies to be statuses, adding ::failed (parameters correct, but could not perform request), ::ok, ::accepted and ::in-progress. It also adds a bunch of utility functions like status/?! (throws the parameter if it's anomaly, returns the parameter otherwise) and macros like status/-?> (threads if an expression is not an anomaly). That's it.

I deliberately avoid trying to do too much here.

All async systems have pitfalls, I'd say core.async's are pretty minor compared to most other systems. You're right that `go` can encourage bloated functions and it would be better if it, for example, handled exception propagation (I would guess every serious core.async user has written their own go-but-with-exception-handing macro, it's not hard but it is unfortunate duplication of effort).

(I've never had to think about the state machine code when debugging and I've done a lot of core async debugging. That part really does seem to just work.)

To be more clear, I didn't mean debugging the generated state machine itself.

What I meant was, the use of the go state machine renders certain debugging techniques useless. E.g., stacktraces are less helpful, and js-debugger is pointless, since you can't guarantee the (js-debugger) will get grouped with the state you're trying to debug.

Frequently print/tap is sufficient, but core.async/go narrows your options.

And here I thought I was too dumb to grok core.async all those years ago (ps I still am)
For me it also lacks observability. It has been a few years since I last used Clojure, but I found manifold to be a much better fit for actual production code that you want to optimize.

I loved ztellman’s “everything must flow” talk on the topic.

Heh, I used to maintain manifold/aleph for a few years after Zach left the Clojure community.
That means we have had various exchanges in the past, but I’m operating under a different username here.

I’ve had several PRs accepted into there, I think manifold got a lot of things right, if only it weren’t for Zach leaving the community.

Unfortunately I left the community for similar reasons, I have a different vision of how the language should evolve than the people in charge, but wasn’t as vocal about it. I suspect there are more people like me.

It’s fine, like Rich Hickey said, it’s his project and we have no right to expect anything.

I am forever glad for what Clojure taught me, it made me a much better developer.

> Unfortunately I left the community for similar reasons

AFAIK Zach was just offered well-paying job that was mostly about dealing with Scala, it wasn't about "how the language should evolve"...

Zach wanted to introduce a community-driven steering community, make the language easier for beginners to adopt, and standardize library choices.

This did not align with Cognitect’s centralized stewardship.

This was around the time that there was some quite some commotion in the community around this, with Rich posting his infamous “Open source is not about you” conclusive post, which was in direct response to @cemerick’s Twitter post: https://x.com/cemerick/status/1067111260611850240