I noticed borkdude posted this thread *and* he is listed as a contributor for this release.
For the longest time, I recall the opposition to async/await support being twofold:
1. adding support would require deep changes across the CLJS compiler (theller, creator of shadow-cljs, once tried and concluded this)
2. macros from libraries like Promesa provided similar convenience
There were some other arguments brought up at the time (e.g. just use core.async, expression-oriented languages aren't a good fit with async/await, etc.), but they were usually specific to one person rather than something you'd see repeated in forums.
In the Clojurians Slack, borkdude once stated he wasn't convinced it'd be impractical to add support. It seems that he eventually took the time and made it happen. Extremely thankful for that.
I believe Borkdude showed it was possible by first implementing async/await in Squint, his alternative* implementation of ClojureScript, and then took those learnings to the core CLJS compiler.
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!
> 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":
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".
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.
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.
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.
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.
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
That was in nov 2018. Two months later Tellman published his "Elements of Clojure". I don't remember the date when he retired from the community. And I don't remember him publicly saying anything about that drama, but I do know for a fact that he joined a team with a ton Scala. How do I know that? Not sure, he might have told someone I know. I might have heard that from him in person, honestly, I don't remember.
I can't speak for Zach, but in 2019 on The REPL podcast #23 (https://www.therepl.net/episodes/23/), at 00:41:01 and 00:45:31, he talks about being a bit unhappy with how the core team communicated about his arity-optimized vec/hash class proposal.
He then talks about Aphyr's and Chas Emerick's similar experiences, and laments how in the earliest days, it was still possible to contribute, and how when core development closed off, it was never articulated up front until "Open Source Is Not About You", which is its own can of worms.
Overall, it's a good and nuanced discussion, but it's obvious he wasn't in unreserved love with the language, so I'm not surprised he left.
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.
Surprised to see Clojure/ClojureScript come up on socials more often all of a sudden. I used it professionally for a few years around ~2012 and like many others moved off JVM and moved into typed [functional] languages.
Is the sudden buzz due to agentic coding? Does it rip through code faster with no type checking and fewer invalid syntax errors and reserved keywords to deal with? are we in for a sexp resurgence?
Personally I moved from typed functional languages to Clojurescript and then Clojure around 10 years ago
Most serious Clojure code bases I'm aware of invest heavily in their test suite so yes you can just add a skill to your AI that tells it the most effective way to use your test suite then send it to the races
Some of my colleagues let the agent interact with the REPL and they report faster performance because the agent is not paying the start up cost on every interaction, personally I've been lazy there its fast enough for me
As you've hinted at Clojure does have less bits that get in the way, everything is true except false and nil, the language does not have order precedence table the core language supports immutable and persistent data structures as default
Everything is an expression nothing is a mixture of operators and expressions
map, reduce, filter are built in and expected in normal code
Code you wrote 10 years ago in Clojure will likely still work today the ecosystem and language authors treat breaking code as taboo
Of all the languages I've used its the least headache inducing and the most freeing in terms of expressing my ideas
Also the defacto reverse debugger Flowstorm is a programming dream
Its a lovely language if you want to be content
The flip side of that is most users take it for granted and don't talk about it much
There are also a lot of commercial Clojure programmers who do not understand the language and as a result not that happy, they often didn't willing choose it, probably not ready for it, I think most Clojurians should have gone through a decade of noticing things in their other languages they didn't like before using it
Rich Hickey the creator of Clojure does famously influential videos on software but that doesn't mean your colleagues have watched them or care
Another feature that plays well with agentic coding is REPL driven development. I don't know why that approach hasn't caught on in more languages that could theoretically support it.
Because most languages don't have a full interactive REPL like for example Common Lisp has. The Python REPL for example is a joke compared to it. Clojure is very closely there, but not quite yet.
Been coding in lots of languages with agentic coding and it performs much better with typed languages since it basically corrects the agent if it does any hallucination errors. Especially during major refactorings. I have been dealing with large untyped python code bases and it sucks with AI since if it's not covered with tests it's such a teadious job to make sure it did not break anything. The stronger the type system the better it is. Also AI model is trained on code and the more popular the language is the better the AI will be at it. ClojureScript while nice is not a major language so I would assume AI would perform worse in it compared to javascript.
But in the end. Choose typed languages or dynamic languages with type hints if you are going for the AI route.
I’ve been using LLMs (the new Deepseek) on Clojure and it works well. The only major pain point is that it sucks at getting the last paren on. There are hooks that will fix that automatically, but I’ve been lazy.
I haven’t tried on anything completely blank, though. My projects have been sort of skeletons where I’m figuring out what I want it to look like, and it’s pretty good at imitating the patterns. Like I write functions to query a particular SQL table, and it can pick up how to use the SQL generation library from that.
It works pretty well. I also have it generating good docstrings though, which might help. Every function gets a “what does it do, why would you use it, what does it expect as args, what does it return” comment. If the function expects a map, the comments specify the keys it needs and so on.
You can also get typing (sort of) by using Malli or similar which does both runtime checking of schemas and serves as documentation of what the expected shape of the arg is. The LLM has been pretty good at falling back to looking at the schema if tests fail because of validation issues.
The logical conclusion is to use "a Haskell" typed language that will ensure every path is considered to guard against AI mistakes. OTOH, clojure repl, expressibility, immutability, and data-driven nature has its own advantages. Tacking on malli (runtime type checking) or spec (types/contracts) helps LLMs avoid type problems altogether or at least confront problems during testing.
Maybe that should be part of LLM benchmarking. If an LLM can handle untyped languages in the context window flawlessly, it would be a worthwhile accomplishment. I think For JS WebStorm loads a TS file for browser fundamentals and it helps drastically with this problem as well, sadly theres no similar concept for Python. Would be neat if someone implemented “Python Header Files” or something so you can provide type hints for these sort of projects.
Equating Clojure and Python just because they both dynamically typed is a very shallow comparison. The actual, practical experience is like a day and night. We have projects in Python, Clojure, Java, C# and Golang. Clojure is by far the least problematic to deal with. Even though they have many ugly and non-idiomatic, very old parts.
LLMs, matter of fact do work beautifully with Clojure, specifically because of the "true" REPL. Python doesn't have a comparable REPL - at best, Python's is an interactive shell.
When you give an LLM a closed loop system where it can evaluate code in a live REPL and immediately observe the results, it stops guessing and starts reasoning empirically.
With Clojurescript, you get a REPL connected to a browser - LLM can navigate any element, click buttons, have the entire page context to inspect and alter - all that without any compilation, without even saving and reloading any code anywhere. It seems you have zero idea how amazingly liberating the actual experience that is. And btw, it's the most token efficient language¹.
> ClojureScript is not a major language
a) There's plenty of Clojure code in the wild now and it's been there even before we started training LLMs.
b) The language is very small syntactically - it gives LLMs fewer ways to fantasize some weird constructs.
c) More popular languages have dozens of ways of doing similar things. Clojure's community is smaller and organized, there's less fragmentation - they don't have twenty different routing libraries, each with their own embedded DSL. You'd ask an LLM to get routing done in Python - every time it will give you a different answer, in Clojure - it would just pick something solid, community-approved, battle-tested and unambiguous.
I really wish people speculating about practical experiences in different languages had really used them before dumping their conclusions on HN. Because theory, papers and books are one thing - the practical, years-long venture into a language stack might be completely different experience. You can't be just like: "Haskell is great because it's pure and lazy and has types" and "Clojure is lame because it's dynamically typed" - the field experience would vary for a bunch of different reasons.
Every language has to be evaluated holistically and specifically for each situation. Just because we call them "general-purpose PLs", we shouldn't be generalizing them all the time.
So, best to just use <insert most popular language> for maximum results? And have the LLM just rehash the majority of what that language's code looks like? Because a lot of say Javascript code I have seen floating around on Github has been absolutely atrocious. That doesn't really give a good basis for LLMs.
I agree that stronger type systems might help. But a good swatch of unit tests should still accompany that code.
The Clojure code I have worked with usually had a damn good array of unit tests as its more of the "clojure" culture to do so.
I haven't kept up with cljs in a long time but I remember it originally being pitched as just clojure on js--at least I think that's how Rich originally described it. My impression was it was just supposed to be, as much as possible, another runtime. This change seems to add in features that are exclusive to cljs and actually conflict with clojure itself as await is already a keyword in clojure.core. Have the two impls diverged over time, or was this feature specifically important enough to the users to overcome the differences?
I wish an alternative to JS for the front end would catch on and be something more than obscure... I'd love to use something like clojurescript, but I struggle to imagine doing so for anything but a personal side project :/ Maybe this is easier to adopt if you're already a clojure shop for the backend?
Are you worried because it’s not a mainstream language and coworkers may not know it, or are you worried about the language itself getting abandoned or being bad or such?
I’ve not used it in production, but I’ve shipped a few side projects and stuff for family members in it. ClojureScripts React wrapper, Reagent, honestly makes more sense to me than React does. I used Hiccup to generate HTML, and your components are just functions within Hiccups DSL (which is really just lists) and it ends up looking incredibly clean. Static things look static, dynamic things are obviously so, and it felt much less magic than regular React.
The only things I found that felt bad were trying to use non-functional components I found on NPM. It’s not a deal breaker, but the code was ugly. Nothing I couldn’t fix with a wrapper, but some JS libraries are heinously ugly in cljs by default.
A big reason why react in clojure can make more sense than react itself is because clojure is much more declarative than JavaScript. There's been a lot of work done on the react end to fix that but the mismatch will always be there.
The type of code you're writing isn't special, it's the way people have written lots of clojure programs for over a decade.
Totally agree. I also think Clojures relative lack of support for custom types leads to elegant APIs.
It’s a breath of fresh air that practically every DSL takes either lists or maps so I can use very similar patterns to build their input rather than “every API wants me to method chain their custom types so every API needs its own special helpers library for common patterns”.
I never realized how much I hate classes until Clojure.
Have you tried typed Clojure? Curious about opinions on that vs having Malli do runtime validation. I tried it when I was a total noob and got overwhelmed. I feel like I have just enough context now to try again, and not sure if it’s nice or the overhead is so high it’s a boondoggle
> Static things look static, dynamic things are obviously so, and it felt much less magic than regular React.
Yes! The moment for me was when React introduced their notorious useEffect/useState hooks API. It immediately jumped out to me as the wrong API, by making static things dynamic. Reagent was really a breath of fresh air. Reagent was a really nice API, though it somewhat encouraged inefficient code.
Don't be afraid, it's great! I certainly wouldn't call it "obscure", I've been using it for 10 years now to compile a complex app into highly-optimized client-side code. And the community is very welcoming and mature.
> I struggle to imagine doing so for anything but a personal side project
Don't imagine. You have any bash scripts your team uses? Rewrite them in Babashka. Start with your own personal scripts first, get a knack for it, feel the benefits (it's not going to be universally better for every case). You have to be very confident about it personally, because people will come for your guidance later.
This is a good strategy for introducing unfamiliar things - pick something less important, rewrite it, let it sit there. If it becomes problematic - easy to revert. If people start liking it, you can add more, and so on.
That's how I sneaked F# in my .net shop years ago - I started writing less important tests in it.
I agree! I have kept an eye on Elm for many years, I think the simplicity and architecture is great, but the language itself never clicked for me.
Then I was made aware of Lustre[1], an Elm inspired web framework in Gleam. I have done two small projects in it now and I really enjoy working with both Lustre and Gleam.
Building UI in HTMX is such a breath of fresh air. I hope it kills the "React" style big complicated SPAs. Its so easy to develop in, its so fast to run, so fast to load.
Ten years ago we were writing ClojureScript for the frontend at work. We weren’t a Clojure shop at all. As my then manager said, picking ClojureScript was part of the hiring bar: people who weren’t interested in functional programming tended not to be good programmers and so we avoided hiring these people.
Check out Mint (https://mint-lang.com), it's s language where everything is built in: small to mid size projects can be built without any third party dependencies and JS interop is easy.
I'm not sure how I feel about this; wasn't part of the point of core.async to push all this stuff into channels? I'm not convinced that having a JS-style async keyword is an upgrade.
This is about using a JS feature without bringing in any additional dependencies like core.async. You don't have to use it and you can still use core.async. This was the most asked for feature in the recent ClojureScript survey.
You're still able to do so, as we've been able to in ClojureScript land for many years already, since ultimately they're just Promises! I don't think that's going away with this new function hints.
The beauty of Clojure/Lisp is that we have a nice macro system. Over the years, people created their own macros to simulate async/await. This is a powerful feature of Lisp, you don’t have to wait for an official release, you can just add that feature to the language yourself. Now we don’t have to use that custom macro anymore, which basically did the same thing.
Is that something people want to get rid of? Back when I did some clojurescript people were pretty proud of being able to have it used automatically. What's the plan to get the same benefits? Or is the argument that the benefits aren't significant 15ish years on?
I would say the community is pretty evenly split between people who hate it, and people who find it practical. I don't see many people really championing it or being proud of it these days.
Well, technically I think most of the community in indifferent. But from the discourse about the topic, I feel like I see pretty even splits.
I'm a very happy Google Closure Compiler user, especially with the "advanced optimizations" flag. It does a level of code elimination and variable renaming at a level that no other JavaScript tool even approaches. Excellent software.
I think it gets a bad rap because you need to write your code in a certain way to avoid the optimizations breaking things. But if you're a disciplined developer, you'll reap some large benefits.
Last i checked, it needed JVM (parts of the library are in Java). Given there are many JS minifiers and optimizers (tree shaking etc.) avaliable in JS itself in 2026, I do not know why we need this huge overhead.
I can finally use this little brain worm that has lived in my head for more than a decade now: IcedCoffeeScript has existed for ages https://maxtaco.github.io/coffee-script/ since well before ES got it.
For the longest time, I recall the opposition to async/await support being twofold:
1. adding support would require deep changes across the CLJS compiler (theller, creator of shadow-cljs, once tried and concluded this)
2. macros from libraries like Promesa provided similar convenience
There were some other arguments brought up at the time (e.g. just use core.async, expression-oriented languages aren't a good fit with async/await, etc.), but they were usually specific to one person rather than something you'd see repeated in forums.
In the Clojurians Slack, borkdude once stated he wasn't convinced it'd be impractical to add support. It seems that he eventually took the time and made it happen. Extremely thankful for that.
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!
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.
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
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".
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.
[0]: https://github.com/IGJoshua/farolero
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.
I loved ztellman’s “everything must flow” talk on the topic.
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.
AFAIK Zach was just offered well-paying job that was mostly about dealing with Scala, it wasn't about "how the language should evolve"...
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
He then talks about Aphyr's and Chas Emerick's similar experiences, and laments how in the earliest days, it was still possible to contribute, and how when core development closed off, it was never articulated up front until "Open Source Is Not About You", which is its own can of worms.
Overall, it's a good and nuanced discussion, but it's obvious he wasn't in unreserved love with the language, so I'm not surprised he left.
(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.)
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.
Is the sudden buzz due to agentic coding? Does it rip through code faster with no type checking and fewer invalid syntax errors and reserved keywords to deal with? are we in for a sexp resurgence?
Most serious Clojure code bases I'm aware of invest heavily in their test suite so yes you can just add a skill to your AI that tells it the most effective way to use your test suite then send it to the races
Some of my colleagues let the agent interact with the REPL and they report faster performance because the agent is not paying the start up cost on every interaction, personally I've been lazy there its fast enough for me
As you've hinted at Clojure does have less bits that get in the way, everything is true except false and nil, the language does not have order precedence table the core language supports immutable and persistent data structures as default
Everything is an expression nothing is a mixture of operators and expressions map, reduce, filter are built in and expected in normal code
Code you wrote 10 years ago in Clojure will likely still work today the ecosystem and language authors treat breaking code as taboo
Of all the languages I've used its the least headache inducing and the most freeing in terms of expressing my ideas
Also the defacto reverse debugger Flowstorm is a programming dream
Its a lovely language if you want to be content
The flip side of that is most users take it for granted and don't talk about it much
There are also a lot of commercial Clojure programmers who do not understand the language and as a result not that happy, they often didn't willing choose it, probably not ready for it, I think most Clojurians should have gone through a decade of noticing things in their other languages they didn't like before using it
Rich Hickey the creator of Clojure does famously influential videos on software but that doesn't mean your colleagues have watched them or care
Live image editing is just pure bliss.
But in the end. Choose typed languages or dynamic languages with type hints if you are going for the AI route.
I haven’t tried on anything completely blank, though. My projects have been sort of skeletons where I’m figuring out what I want it to look like, and it’s pretty good at imitating the patterns. Like I write functions to query a particular SQL table, and it can pick up how to use the SQL generation library from that.
It works pretty well. I also have it generating good docstrings though, which might help. Every function gets a “what does it do, why would you use it, what does it expect as args, what does it return” comment. If the function expects a map, the comments specify the keys it needs and so on.
You can also get typing (sort of) by using Malli or similar which does both runtime checking of schemas and serves as documentation of what the expected shape of the arg is. The LLM has been pretty good at falling back to looking at the schema if tests fail because of validation issues.
LLMs, matter of fact do work beautifully with Clojure, specifically because of the "true" REPL. Python doesn't have a comparable REPL - at best, Python's is an interactive shell.
When you give an LLM a closed loop system where it can evaluate code in a live REPL and immediately observe the results, it stops guessing and starts reasoning empirically.
With Clojurescript, you get a REPL connected to a browser - LLM can navigate any element, click buttons, have the entire page context to inspect and alter - all that without any compilation, without even saving and reloading any code anywhere. It seems you have zero idea how amazingly liberating the actual experience that is. And btw, it's the most token efficient language¹.
> ClojureScript is not a major language
a) There's plenty of Clojure code in the wild now and it's been there even before we started training LLMs.
b) The language is very small syntactically - it gives LLMs fewer ways to fantasize some weird constructs.
c) More popular languages have dozens of ways of doing similar things. Clojure's community is smaller and organized, there's less fragmentation - they don't have twenty different routing libraries, each with their own embedded DSL. You'd ask an LLM to get routing done in Python - every time it will give you a different answer, in Clojure - it would just pick something solid, community-approved, battle-tested and unambiguous.
I really wish people speculating about practical experiences in different languages had really used them before dumping their conclusions on HN. Because theory, papers and books are one thing - the practical, years-long venture into a language stack might be completely different experience. You can't be just like: "Haskell is great because it's pure and lazy and has types" and "Clojure is lame because it's dynamically typed" - the field experience would vary for a bunch of different reasons.
Every language has to be evaluated holistically and specifically for each situation. Just because we call them "general-purpose PLs", we shouldn't be generalizing them all the time.
___
¹ https://martinalderson.com/posts/which-programming-languages...
I agree that stronger type systems might help. But a good swatch of unit tests should still accompany that code.
The Clojure code I have worked with usually had a damn good array of unit tests as its more of the "clojure" culture to do so.
I’ve not used it in production, but I’ve shipped a few side projects and stuff for family members in it. ClojureScripts React wrapper, Reagent, honestly makes more sense to me than React does. I used Hiccup to generate HTML, and your components are just functions within Hiccups DSL (which is really just lists) and it ends up looking incredibly clean. Static things look static, dynamic things are obviously so, and it felt much less magic than regular React.
The only things I found that felt bad were trying to use non-functional components I found on NPM. It’s not a deal breaker, but the code was ugly. Nothing I couldn’t fix with a wrapper, but some JS libraries are heinously ugly in cljs by default.
The type of code you're writing isn't special, it's the way people have written lots of clojure programs for over a decade.
It’s a breath of fresh air that practically every DSL takes either lists or maps so I can use very similar patterns to build their input rather than “every API wants me to method chain their custom types so every API needs its own special helpers library for common patterns”.
I never realized how much I hate classes until Clojure.
Have you tried typed Clojure? Curious about opinions on that vs having Malli do runtime validation. I tried it when I was a total noob and got overwhelmed. I feel like I have just enough context now to try again, and not sure if it’s nice or the overhead is so high it’s a boondoggle
Yes! The moment for me was when React introduced their notorious useEffect/useState hooks API. It immediately jumped out to me as the wrong API, by making static things dynamic. Reagent was really a breath of fresh air. Reagent was a really nice API, though it somewhat encouraged inefficient code.
Don't imagine. You have any bash scripts your team uses? Rewrite them in Babashka. Start with your own personal scripts first, get a knack for it, feel the benefits (it's not going to be universally better for every case). You have to be very confident about it personally, because people will come for your guidance later.
This is a good strategy for introducing unfamiliar things - pick something less important, rewrite it, let it sit there. If it becomes problematic - easy to revert. If people start liking it, you can add more, and so on.
That's how I sneaked F# in my .net shop years ago - I started writing less important tests in it.
https://blisswriter.app/
https://blog.nestful.app/p/how-we-dropped-vue-for-gleam-and
Then I was made aware of Lustre[1], an Elm inspired web framework in Gleam. I have done two small projects in it now and I really enjoy working with both Lustre and Gleam.
[1] https://github.com/lustre-labs/lustre
https://hypermedia.systems/
I came to the conclusion that the best frontend is no frontend.
Congratulations on the release :-)
core.async isn't going anywhere, if async/await works better than promise based implementation, core.async will get an update in it's .cljs parts
https://clojurescript.org/guides/promise-interop#using-promi...
Well, technically I think most of the community in indifferent. But from the discourse about the topic, I feel like I see pretty even splits.
I think it gets a bad rap because you need to write your code in a certain way to avoid the optimizations breaking things. But if you're a disciplined developer, you'll reap some large benefits.