I wrote Clojure for about five years. Left when I changed jobs, not because I wanted to. It's genuinely one of the most productive languages I've used, and I still miss the REPL-driven workflow.
One thing I built: defun https://github.com/killme2008/defun -- a macro for defining Clojure functions with pattern matching, Elixir-style. Still probably my favorite thing I've open sourced.
tombert [3 hidden]5 mins ago
Clojure is such a fun language to write; it has great concurrency tools from the get go, and since it has access to the entire Java ecosystem you're never hurting for libraries.
I find myself missing Clojure-style multimethods in most languages that aren't Clojure (or Erlang); once multimethods clicked for me, it seemed so blatantly obvious to me that it's the "correct" way to do modular programming that I get a little annoyed at languages that don't support it. And core.async is simply wonderful. There are lots of great concurrency libraries in lots of languages that give you CSP-style semantics (e.g. Tokio in Rust) but none of them have felt quite as natural to me as core.async.
I haven't had a chance to touch Clojure in serious capacity in awhile, though I have been tempted to see if I can get Claude to generate decent bindings for Vert.x to port over a personal project I've been doing in Java.
dgb23 [3 hidden]5 mins ago
I like it! Really nice API.
I had an idea about writing something similar, but for multimethods, but never got around thinking it through and trying it out.
The way defmulti and defmethod work is that they do a concurrency safe operation on a data structure, which is used to dispatch to the right method when you call the function.
My hunch is that it should be possible to do something similar by using core match. What I don't know is whether it's a good idea or a terrible one though. When you're already doing pattern matching, then you likely want to see everything in one place like with your library.
LouDNL [3 hidden]5 mins ago
It's good to read that Clojure is getting more and more exposure. I write Clojure fpr my day job and wouldn't want to swap it for anything. The community is small but very helpfull and easy reachable. The learning curve is steap indeed, but very much worth it!
thunky [3 hidden]5 mins ago
Clojure has some pretty big downsides last i looked:
- syntax is hard to read unless you spend a lot time getting used to it
- convention for short var names makes it even harder
- function definition order makes it even harder
- too dynamic for most people's taste
- no type safety
- the opposite of boring
- no clear use case to show it clearly beating other languages
- niche with small community and job market
- JVM
For all those reasons its a hard sell for most imo.
manoDev [3 hidden]5 mins ago
These might be theoretical issues that people without experience worry about, but let me share what I've witnessed in practice working almost a decade with Clojure at Nu.
We mostly hired people with no previous Clojure experience. Majority of hires could pick up and get productive quickly. People fresh out of college picked it up faster. I even had a case of employee transitioning careers to S.E., with no previous programming experience, and the language was a non issue.
I can't remember an instance where the language was a barrier to ship something. Due to reduced syntax surface and lack of exotic features, the very large codebase followed the same basic idioms. It was often easy to dive into any part of the codebase and contribute. Due to the focus on data structures and REPL, understanding the codebase was simply a process of running parts of a program, inspecting its state, making a change, and repeat. Following this process naturally lead to having a good test suite, and we would rely on that.
Running on the JVM is the opposite of a problem. Being able to leverage the extensive JVM ecosystem is an enormous advantage for any real business, and the runtime performance itself is top tier and always improving.
The only hurdle I could say I observed in practice was not having a lot of compile time guarantees, but since it was a large codebase anyway, static guarantees would only matter in a local context, and we had our own solution to check types against service boundaries, so in the end it would've been a small gain regardless.
asa400 [3 hidden]5 mins ago
> the opposite of boring
I have to push back on this one, respectfully.
Clojure is easily the most boring, stable language ecosystem I’ve used. The core team is obsessed with the stability of the language, often to the detriment of other language values.
This attitude also exists among library authors to a significant degree. There is a lot of old Clojure code out there that just runs, with no tweaks needed regardless of language version.
Also, you have access to tons of battle tested Java libraries, and the JVM itself is super stable now.
I won’t comment on or argue with your other points, but Clojure has been stable and boring for more than a decade now, in my experience.
thunky [3 hidden]5 mins ago
What I meant by that is the metaprogramming capabilities that often get cited for allowing devs to create their own domain specific "mini languages". To me that's a "creative" way to write code because the end result could be wildly different depending on who's doing the writing. And creativity invites over-engineering, over-abstraction, and hidden costs. That's what I meant by the "opposite of boring".
hatefulheart [3 hidden]5 mins ago
I read comments like these in bewilderment.
Have you worked for a company that hasn’t created its own, as you put it “mini language”?
Have you worked for a company that doesn’t indulge in over engineering, over abstraction and hidden cost?
Do you actually do programming for a job at all?
thunky [3 hidden]5 mins ago
speaking of bewilderment, i'm not sure at all what you're getting at here.
because programmers suck we should make tools that make it easier for them to suck?
tines [3 hidden]5 mins ago
We should make it easier for them to suck less. It's like providing free needles. Druggies gonna drug, might as well help them drug safely :)
dgb23 [3 hidden]5 mins ago
You linked me to this comment from another one and I have to agree with this sentiment.
Creating these mini DSLs is something that requires a lot of thought and good design. There is a danger here as you pointed out sharply.
But I have some caveats and counter examples:
I would say the danger is greater when using macros and far less dangerous when using data DSLs. The Clojure community has been moving towards the latter since a while.
There are some _very good_ examples of (data-) DSLs provided by libraries, such as hiccup (and derived libraries), reitit, malli, honeysql, core match, spec and the datalog flavor of Clojure come to mind immediately (there are more that I forget).
In many cases they can even improve performance, because they can optimize what you put into them behind the scenes.
midnight_eclair [3 hidden]5 mins ago
so many similar conversations happening, it's refreshing xD
In practice, though, most developers don’t do that.
There’s a rule of thumb: write a macro as a last resort.
It’s not hard to stick to it. In general, you can go a long, long way with HOFs, transducers, and standard macros before a hand-rolled macro would serve you better.
Oreb [3 hidden]5 mins ago
> syntax is hard to read unless you spend a lot time getting used to it
That’s pretty much exactly the opposite of how I always felt. Perhaps because I’m not a programmer by education, I always struggle to remember the syntax of programming languages, unless I’m working in them all the time. After I return to a language after working in other languages for a while, I always have difficulties remembering the syntax, and I spend some time feeling very frustrated.
Clojure and Lisps more generally are the exception. There is very little syntax, and therefore nothing to remember. I can pick it up and feel at home immediately, no matter how long I’ve been away from the language.
tombert [3 hidden]5 mins ago
I don't think the syntax is hard to read in any kind of objective sense, it's just different than most mainstream languages. Greek would be hard for me to read too, but that's not because it's necessarily harder to read than English, just that I don't really know Greek.
I agree with the short variable name convention, that's annoying and I wish people would stop that.
Everyone complains about a lack of type safety, but honestly I really just don't find that that is as much of an issue as people say it is. I dunno, I guess I feel like for the things I write in Clojure, type issues manifest pretty early and don't really affect production systems.
The clearest use-case I have for Clojure is how much easier it is to get correct concurrent software while still being able to use your Java libraries. The data structures being persistent gives you a lot of thread safety for free, but core.async can be a really nice way to wrangle together tasks, atoms are great for simple shared memory, and for complicated shared memory you have Haskell-style STM available. I don't remember the last time I had to reach for a raw mutex in Clojure.
Good concurrency constructs is actually how I found Clojure; I was looking for a competent port of Go-style concurrency on the JVM and I saw people raving about core.async, in addition to the lovely persistent maps, and immediately fell in love with the language.
Also, I really don't think the JVM is a downside; everyone hates on Java but the fact that you can still import any Java library means you're never blocked on language support. Additionally, if you're willing to use GraalVM, you can get native AOT executables that launch quickly (though you admittedly might need to do a bit of forward-declaration of reflection to get it working).
Antibabelic [3 hidden]5 mins ago
The JVM is one of the major selling points of Clojure. You can "write once, run anywhere" and benefit from Java's massive ecosystem, all without having to use a Blub language. Modern JVM implementations are also incredibly fast, often comparable in performance to C++ and Go.
thunky [3 hidden]5 mins ago
i don't think you're wrong necessarily...but rust, golang, zig, mojo, etc are gaining popularity and imo they wouldn't be if they were JVM languages.
Antibabelic [3 hidden]5 mins ago
It's almost as if different tools exist for solving different problems. Clojure is "Lisp on the JVM". That's the core premise behind the language. Rust is a "systems programming language with a focus on type and memory safety". This is an apples-to-oranges comparison. They offer different benefits while providing different drawbacks in return. Their ecosystems are likewise very different, in each case more closely tailored to their particular niche.
thunky [3 hidden]5 mins ago
understood, i'm just pointing out that people seem to prefer the apple over the orange.
dgb23 [3 hidden]5 mins ago
> no type safety
That's fair if you're looking at it from a performance perspective.
Not entirely fair if you look at it from a perspective of wanting fast feedback loops and correctness. In Clojure you get the former via the REPL workflow and the latter through various other means that in many cases go beyond what a typical type system provides.
> the opposite of boring
It's perhaps one of the most "boring in a good way" languages I ever used.
thunky [3 hidden]5 mins ago
> It's perhaps one of the most "boring in a good way" languages I ever used.
This comment is like saying you can appreciate wine, but you think it should have less grapes.
hombre_fatal [3 hidden]5 mins ago
You also need to learn a new tool to write lisp, like paredit.
While it's amazing once you've learned it, and you're slurp/barfing while making huge structural edits to your code, it's a tall order.
I used Clojure for a long time, but I can't go back to dynamic typing. I cringe at the amount of time I spent walking through code with paper and pencil to track things like what are the exact keyvals in the maps that can reach this function that are solved with, say, `User = Guest | LoggedIn` + `LogIn(Guest, Password) -> LoggedIn | LogInError`.
Though I'm glad it exists for the people who prefer it.
midnight_eclair [3 hidden]5 mins ago
i'm surprised anybody coming from clojure would say this.
you absolutely do NOT need to learn paredit to write lisp, any modern vim/emacs/vscode plugin will just handle parentheses for you automatically.
that said, if you do learn paredit style workflow - nobody in any language in any ide will come even close to how quickly you can manipulate the codebase.
hombre_fatal [3 hidden]5 mins ago
I realize how lame it is to relitigate Clojure's downsides every time it comes up. I fell into the same trap that annoys me about Elm threads: people who haven't used it in a decade chiming in to remind everyone they didn't like some aspect of it. Wow, such contribution.
It's like seeing that a movie is playing at the theater so you show up only to sit down next to people to explain your qualms with it, lolz. Sometimes you need to let others enjoy the show.
The OP of this thread even said all that needed to be said "The learning curve is steep but very much worth it" yet we're trapped in this cycle because someone had to embellish it with a listicle.
I take my post back.
thunky [3 hidden]5 mins ago
I didn't realize it was a no-no to share opinions here.
Plus to be fair we're having this discussion in the context of an article from 2021 that just rose to front page of HN, only to repeat the same set of pros we've been hearing about Clojure for ages (code as data, repl, etc).
Probably should expect some dissenting opinions.
jimbokun [3 hidden]5 mins ago
Most of those seem very subjective with many people having the exact opposite opinion.
thunky [3 hidden]5 mins ago
yes it's just my opinion. but Clojure's market share is tiny so there must be something to that.
"The TIOBE index measures how many Internet pages exist for a particular programming language."
For some reason I doubt this is in any way representative of the real world. Scratch, which is a teaching language for children, bigger than PHP? Which is smaller than Rust? Yeah, these are results you get when you look at the Internet, alright.
thunky [3 hidden]5 mins ago
Sure that index isn't great (I think it's basically a regurgitation of Google Trends), but I don't think you're suggesting Clojure is actually a popular language are you? Which is the only point I'm trying to make (that it isn't popular).
Antibabelic [3 hidden]5 mins ago
Clojure is reasonably popular as far as programming languages go. It's not difficult to get a job as a Clojure developer, particularly in certain sectors (fintech and healthcare are the heaviest Clojure users). Of course C++, Java, C# and PHP dwarf both Clojure and Rust by several orders of magnitude.
embedding-shape [3 hidden]5 mins ago
If anything, I think that makes Clojure better. Almost no one in the community is doing stuff to serve "lowest common denominator", compared to how most of JS/TS development is being done, which is a breeze of fresh air for more senior programmers.
Besides, the community and ecosystem is large enough that there are multiple online spaces for you to get help, and personally I've been a "professional" (employed + freelancing) Clojure/Script developer for close to 7 years now, never had any issues finding new gigs or positions, also never had issues hiring for Clojure projects either.
Sometimes "big enough" is just that, big enough :)
joshlemer [3 hidden]5 mins ago
Spinning dwindling adoption as a good thing because it "unburdens community from serving lowest common denominator use-cases" is exactly the kind of of downplaying/deflection of every issue that I'm talking about, which constantly happens in the Clojure community. It's such an unhealthy attitude to have as a community and it holds it back from actually clearly seeing what the issues are and coming up with solutions to them.
Every problem people face is "not a problem" or "actually a good thing" or, maybe if all else fails we can make users feel bad about themselves. Clojure is intended for "well experienced, very smart developers". Don't you know, our community skews towards very senior developers! So if you don't like something, maybe the problem is just that you're not well experienced enough? Or, maybe what you work on is just too low-brow for our very smart community!
embedding-shape [3 hidden]5 mins ago
> It's such an unhealthy attitude to have as a community
How about just "different"? Turtle want to teach everyone to program, that's fine, just another way of building and maintaining a language. Clojure is clearly not trying to cater to the "beginner programmer" crowd, and while you might see it as "unhealthy attitude", I'd personally much prefer to realize having many different languages for different people is way better than every language trying to do the same thing for the same people. Diversity in languages is a benefit in my eyes, rather than a bad thing.
thunky [3 hidden]5 mins ago
I'm glad it works for you and many others and gives you a good living. Nothing wrong with that. I wasn't trying to attack it or anyone that uses it, just stating why I never warmed up to it and projecting why I think it hasn't become popular.
IBCNU [3 hidden]5 mins ago
JVM + Syntax is upside for me.
Type's are for compilers ;) jk. I'm fully lover or type's but removing the constraint is easy in clojure. teams resist.
<3 the opposite of boring.
rockyj [3 hidden]5 mins ago
I am a Clojure fan and would love to use it. But you are right, we live in a real world where money talks and most organizations want to see developers as cheap, replaceable commodities.
Not to mention in a post AI world, cost of code generation is cheap, so orgs even need even fewer devs, combine all this with commonly used languages and frameworks and you need not worry about - "too valuable to replace or fire".
Having said that - there may be a (very) small percentage of orgs which care about people, code crafting and quality and may look at Clojure as a good option.
virgil_disgr4ce [3 hidden]5 mins ago
> - syntax is hard to read unless you spend a lot time getting used to it
This is only true if you assume C-like syntax is the "default."
But regardless of that, I'd argue that there's much less syntax to learn in LISPy languages. The core of it is really just one single syntactic concept.
regularfry [3 hidden]5 mins ago
But that's exactly the root of the complaint. Because there's (for the sake of argument) only one syntactic concept, there's no bandwidth for structural concepts to be visible in the syntax. If you're used to a wide variety of symbols carrying the structural meaning (and we're humans, we can cope with that) then `)))))))` has such low information density as to be a problematic road bump. It's not that the syntax is hard to learn, it's that everything else you need to build a program gets flattened and harder to understand as a result.
Even among lisps this has been problematic, you can look at common lisp's LOOP macro as an attempt to squeeze more structural meaning into a non-S-expression format.
gorjusborg [3 hidden]5 mins ago
This guy gets it.
The syntax argument is such a tired argument. With LISPy language there is almost zero syntax, it's pretty much executable AST.
Because of this, formatting matters a lot, but I don't think that's too different than other languages.
If you think LISP is hard to read, you are someone who could most benefit from branching out to a non-Algol lineage language.
Also, the little syntax present is pretty much timeless. Learn once and its yours for the next 50 years.
ux266478 [3 hidden]5 mins ago
> With LISPy language there is almost zero syntax
To be pedantic, this isn't quite correct. Syntax isn't countable like that. What S-expressions are light on is production rules. At their most basic they have IIRC 7 production rules but there are absolutely 0 languages based on s-expressions which are that simple, since it doesn't give you anything like quasiquotes, vectors, Lisp 2 function resolution, etc. Reader macros make matters much worse.
What we can say is that they are constructively simple, but not particularly unique in that. Once you get into real sexpr languages they aren't simpler than horn clauses, and are constructively more complex than languages like Brainfuck and Forth.
thunky [3 hidden]5 mins ago
> The syntax argument is such a tired argument.
It's repeated a lot because it's true. The collective developer world has decided that LISP syntax is not the preference. Good if you prefer it, but you're the in the overwhelming minority.
You probably love it but to me it looks like a wall of text. Sure I can figure out what it does, but it gives me a headache.
epgui [3 hidden]5 mins ago
It’s not true, unless you have an unusual definition of “syntax”. Lisp basically has the most minimal syntax possible, by design.
To use the right words: it’s not a syntax issue, it just looks unfamiliar to you.
joshlemer [3 hidden]5 mins ago
I think this is kind of misleading. Yes s-expressions have very simple syntax in and of themselves. But s-expressions are not all that's required to get all the control structures in Clojure. You need to memorize all the special forms and all the standard macros that people use on a day to day basis. And they're just as hard (actually IME harder) to memorize as any other syntax. let, cond, record, if, condp, let-if, fn, def, defn, loop, recur, if-some, when-let, for, ->, ->>, as->>, cond-> ...
To this day I have to look up whenever I get back into clojure what the "syntax" is of ns, require, import, etc.
epgui [3 hidden]5 mins ago
Remembering macros is much more like remembering functions than syntax.
midnight_eclair [3 hidden]5 mins ago
i can link you similarly undecipherable walls of text in rust and zig and c
but i bet if you sat down a junior developer not yet entrenched in any style yet, they'd be able to grok lisp code MUCH faster than the intricacies of syntax of the other alternatives ¯\_(ツ)_/¯
hrmtst93837 [3 hidden]5 mins ago
Clojure on the JVM adds niche-language overhead to old deployment pain, so you get bleeding-edge bugs and mid-90s release rituals in the same stack. If you want to onboard new hires to Clojure expect to spend time on editor config and build tooling before they can even trust a stack trace. You still inherit Java's GC quirks without much type-driven tooling.
greekrich92 [3 hidden]5 mins ago
Moby Dick is too hard to read. They should make it shorter with a limited vocabulary.
Cthulhu_ [3 hidden]5 mins ago
I kinda get where you're trying to go, but is Moby Dick style writing the best way to convey information?
That is, prose is good for entertainment, but less so for conveying information, even less so for exactness.
greekrich92 [3 hidden]5 mins ago
Point being that things of high quality that are enriching sometimes require an investment
bitwize [3 hidden]5 mins ago
> It's good to read that Clojure is getting more and more exposure.
I nominate this sentence for "best inadvertent freestyle rap on Hackernews".
IBCNU [3 hidden]5 mins ago
AI augmented Repl driven dev has got me back into Clojure and it's been changing my life (full on JVM nerd: Kotlin mostly on the backend).
The syntax is the best in the world (how computer's really operate?) but it's always been a pain to setup the tooling for me. I'm dumb like that. Now with AI it's become super easy to get back into the REPL and I'm in heaven.
Totally moving it back into workflow and proposing to bring it back into the dayjob.
packetlost [3 hidden]5 mins ago
> how computer's really operate?
I don't know exactly what you mean by this, but Clojure syntax is not really anywhere close to how computers actually process instructions.
Clojure is very nice though.
nmil [3 hidden]5 mins ago
May I ask what your workflow actually looks like? There's been a fair bit of clojureposting over the last few days, and I've decided to jump in and learn.
I love the idea of an AI integrated repl (like what Jeremy Howard and team have done with solveit), it's far more in line with my preferred vision of the AI augmented future of coding. Less "swarm of agent" more, "learn with the agent".
IBCNU [3 hidden]5 mins ago
Sure!
Setup is:
- nvim --listen /tmp/nvim — starts Neovim with a socket Claude can connect to
- /mcp in Claude Code — enables the Neovim MCP server, gives Claude direct control of Neovim
- lein repl in the nvim terminal
- Claude reads .nrepl-port, runs :ConjureConnect — REPL is live!
The loop is so dope:
- Claude writes code directly into my .clj files
- Then evals it into the running process via Conjure
- Sees the result in the REPL, iterates if wrong, all in the same conversation turn
- wrap-reload middleware means the web server hot-reloads changed namespaces on the next request
zingar [3 hidden]5 mins ago
Hmmmm… seeing this workflow makes me wonder if I can do this with Ruby (the integration between agent and repl)
embedding-shape [3 hidden]5 mins ago
Absolutely, spin up a TCP server in Ruby, make it run `eval` on what it receives and tell the agent how to call it with netcat or whatever, works like a charm :)
IBCNU [3 hidden]5 mins ago
Also I love your take.
Feel like a holy grail to me to back in the hot seat with repl driven but I can drop in and figure things out... all on the JVM. Madness!
InvOfSmallC [3 hidden]5 mins ago
My pain point (which I admit didn't recheck if someone did something about it), is an interoperable example of how to use Spring (n.1 framework for many enterprises) with Clojure.
Something where I feel Kotlin did better.
For me the best way to introduce something like this is that I can actually start with small software increments on a Spring Java project.
midnight_eclair [3 hidden]5 mins ago
every time i go back to writing non-clojure code outside of repl-driven environment i feel like a cave man banging rocks against each other
no amount of ide smartness or agentic shenanigans is going to replace the feeling of having development process in sync with your thought process
jimbokun [3 hidden]5 mins ago
You just made me wonder if REPL driven development and LLMs can be better combined somehow.
SatvikBeri [3 hidden]5 mins ago
I use a REPL in tmux. That lets Claude code read/write to it easily, as well as letting me take manual control to investigate.
thom [3 hidden]5 mins ago
This is already a strong theme in the Clojure ecosystem, e.g.:
Slightly off topic, but I find it to be a testament of how software has already eaten the world when friggin Michelin has a tech blog. What's next? General Electric releasing a frontend framework?
brabel [3 hidden]5 mins ago
Toyota has an open source game engine written in Flutter!
I'd love to work with Clojure. I have the misfortune of working on something that is stuck on java1.8 and Groovy, part of the issue is the code quality is a disaster (json and xml parsed with regex...). At least with Clojure I'd get to enjoy the repl workflow and usable text editor (emacs). I also just enjoy working with sexps.
dkarl [3 hidden]5 mins ago
Honestly, if I could quit my job for six months and work in a codebase like yours, I'm extremely curious what I could accomplish with AI.
We have a codebase at work that was "stuck." We've consistently done minor library upgrades, but no major upgrades in several years, and was recognized as a major piece of technical debt / minor disaster for almost two years, in that we urgently needed to dedicate an engineer to it for a month or more to bring it up to date. We also suspected that framework upgrades would improve performance enough to save us a little bit in operating costs. I got curious, created a branch, and threw Claude at it. Claude knocked it out in a couple of days while I mostly worked on other things. Then we dedicated several engineer days to doing extra manual testing. Done and deployed. Now we're ready to experiment with giving it less resources to see if the performance improvement holds up in practice.
This codebase was only about 200k lines of code, so probably smaller than yours. Really curious how it would go with a larger codebase.
EDIT: Claude may only have taken a couple of days because I was only checking in occasionally to give it further instructions. I don't know how fast it would have been with my complete attention.
eduction [3 hidden]5 mins ago
The latest clojure still works with Java 8 fwiw... Although I believe they are looking at moving to 17 soon as minimum.
erfgh [3 hidden]5 mins ago
Can someone enlighten me about the REPL that lispers keep raving about? Isn't it more-or-less the same as the Python REPL?
midnight_eclair [3 hidden]5 mins ago
the benefit of lisp in an editor with integrated repl environment is that you get to see immediate feedback by easily evaluating chunks of your code as you navigate it, without copying stuff back and forth between the editor and the repl
and the benefit of lisp comes from the fact that every expression is fully delineated by parentheses, so you don't need to select any text, find the start or the end of any syntactic construction, you just eval the form you're standing at and see the result
or just as easily you can wrap that form, bind the locals to test values and eval that to see what changes
arafeq [3 hidden]5 mins ago
simplest way to think about it: the python repl is a scratch pad next to your code. the clojure repl IS your running application. you're rewiring the plane mid-flight instead of landing, making changes, and taking off again. once you experience that feedback loop you genuinely can't go back.
stephbook [3 hidden]5 mins ago
Doesn't sound too different from Typescript breakpoints attached to a running website with "Hot Module Replacement."
joshlemer [3 hidden]5 mins ago
Yeah to be honest after trying for many years to understand what is so special about this famous Clojure REPL I struggled to see how it was that different in practice from Python or other languages. In Python you can also highlight a section code and send it to the console to be evaluated.
I think debuggers are just better anyways. When I got into the weeds trying to do this interactive REPL workflow on an actually running webapp it was a big mess. You have to write custom code all the time to basically capture data and save it into global variables, so that you can inspect them (as opposed to a debugger where you can just set a breakpoint and inspect all the variables in the middle of the precise spot in the request-response cycle).
I think maybe this was ahead of its time when it came out in 2008 or got popular in 2010-2015, but nowadays I am not seeing what's specifically more productive about the Clojure REPL than the interactive development experiences available in Laravel, Python, JS, etc.
slifin [3 hidden]5 mins ago
As someone who loves the clojure repl I do somewhat agree with debuggers are better
If it was like a choice, Clojure has my favourite reverse debugger flowstorm which also exposes the runtime information of your program programmatically
So you can code cool visual programs against the runtime values of your first program
Like I built an emulator at work that simulates our program in production as a flowstorm plugin then you can step through the program frame by frame
I love taking the guess work out of production issues, just get the computer to show you exactly how everything went down
It's like a rewindable movie made up of thousands or millions of frames of the execution of your program
stephbook [3 hidden]5 mins ago
To be fair to Clojure, I think it's perfect for rule-engines, where the users supply "business logic" at runtime.
That's where "code is data" shines and you can manipulate it at will. For the rest, I'm glad I have "code as text" in my editor, where I can see and inspect it far more easily than one line at a time.
thom [3 hidden]5 mins ago
Almost exactly, it's mostly how you use the REPL that differs, and then only because of what different editors prioritise. When I'm in Emacs, all my work happens against a running REPL - when I open or save a file, it's reloaded. Any tests loaded in the REPL rerun on every save, within that live instance. If I drop into the debugger, it's against that live instance. I can swap in mock components to a running system, go check stuff in a browser (even jack into a live webpage with ClojureScript), all in one long running instance. I have struggled to recreate this kind of setup as smoothly in Python with any editor (pytest doesn't want to run this way, and IPython's autoreload doesn't feel as reliable), but I do probably write more REPLy code in Python than most, so all my model training and optimisation runs during development happen in pausable background threads in IPython etc.
All that said, 90% of the time you still just eval a bit of a code to see what happens and that's the same between the two languages.
(you can theoretically pass "reload": true (or similar option) in launch.json for auto reload, tho I haven't felt the need to use that in my workflows.)
fredrikholm [3 hidden]5 mins ago
You evaulate code within your editor against the REPL, seeing the output in the same window you're writing in (perhaps in a different buffer).
The cycle is:
1. Write production code.
2. Write some dummy code in the same file (fake data, setup).
3. Evaluate that dummy code. See what happens.
4. Modify code until satisfied.
Your feedback loop is now single digit seconds, without context switching. It's extremely relaxing compared to the alternatives (rerunning tests, launching the program with flags, what have you).
embedding-shape [3 hidden]5 mins ago
Indeed. For people used to the "typical REPL" from Ruby, Python and alike, the best comparison I've found is this:
"Typical REPL" workflow: Have one editor open, have one REPL open, have one terminal open that runs the application. One change is typically: Experiment in the REPL window, copy-paste into your editor, write tests, restart application (lose all state), setup reproduction state, test change. Or something like this.
In a Clojure REPL workflow, you'd do something like: Have one editor open, this starts the REPL and often the application in the background too. One change is typically: Edit code, evaluate that snippet of code (which sends it to the REPL and the running application), write tests, evaluate them too in the editor, if you're happy, hit CTRL+S and you're done. Application still has the existing state, no restarts needed and you essentially never have to leave the editor window/pane.
Of course, others might have slightly different workflows, but for myself and many (most?) other Clojure developers I've observed in the wild, this is pretty much the standard.
wwweston [3 hidden]5 mins ago
Being someone who’s used to the “typical REPL” flow, I’m not sure I grasp what’s going on with the no-restarts. The implications I think I see are:
* Clojure is built different in terms of hot code reloading
* the REPL is its own application process in languages Ruby or Python, but in Clojure it’s sortof a client for the system
Is that right? Is there more to it?
embedding-shape [3 hidden]5 mins ago
Yeah, Clojure code is typically built with this in mind, and the data structures as well.
So in a JavaScript server, you might have the database connection set in some config/thing you pass around to request handlers to use, and if you change a request handler, you typically stop the entire application, then start it again. Then the database connection (and the entire config in fact) would need to again connect.
In a Clojure application, first you either start the repl from the server, or start the server from the repl, then you might instead keep the database connection in an "atom" that you keep around for as long as you have the repl/server running. And instead of restarting the process when you change a handler, you'd only change the handler.
And yeah, the libraries and ecosystem at large is built with this (mostly) in mind for everything. The language also supports this by letting you "redefine" basically anything in a running Clojure application, which would be harder in other languages.
I've done some experiments years ago for doing the same in JavaScript, and it kind of works, but every library/framework in the ecosystem is not built with this in mind, so you'll be writing a lot of your own code. Which, now with LLMs, maybe is feasible, but can't say it was at the time exactly.
tvink [3 hidden]5 mins ago
You're on to something. It's the lisp machine of it all. Hot reloading is nothing that requires anything special, so you can redefine a callback or dependency with ease in the repl and the system chugs along. You can theoretically do something similar in ruby, but it's the opposite of elegant, you'd be forced to re implement methods with different dependencies etc. It's also a function of being "functional" in the lisp sense, that things are lists, and lists can be replaced, functions or otherwise.
The fun way to get a feel for lisp machines is emacs, it's so easy to fall of a language and especially hand-coding in a language if you don't have to.
norir [3 hidden]5 mins ago
You don't need a repl for this workflow and it can be easily implemented in any language. `ls *.MY_LANG | entr -c run.sh` You get feedback whenever you save the file.
Personally, I find waiting more than 200ms unacceptable and really < 50ms is ideal. When the feedback is very small, it becomes practical to save the file on every keystroke and get nearly instantaneous results with every input char.
rienbdj [3 hidden]5 mins ago
Is this similar to Unison scratch file driven development?
An example of me solving an Advent of Code with clojure and repl. You can see i never interact with the repl directly, I just send code to it via my editor and get results inline.
neutronicus [3 hidden]5 mins ago
The two big things are:
1. REPL is automatically compiled into running systems
2. Great hot-reloading support
So it's generally very easy to "poke" at a running system, and the whole dev process assumes you will do this.
TBH, these days it is largely possible in a C++ debugger. Less so 10 years ago, though.
sammy0910 [3 hidden]5 mins ago
it is very similar, but it is easier to evaluate sub-expressions thanks to the unique syntax of lisp.
More or less, yes. It's more about the approach to the repl and how it is leveraged in development, or even jacking in to a running system and modifying it as it is running.
0x1ceb00da [3 hidden]5 mins ago
What is the y axis in first chart? What is the data source?
dgb23 [3 hidden]5 mins ago
They publish their yearly surveys on their official website. The graph seems to be from State of Clojure 2020:
One thing I built: defun https://github.com/killme2008/defun -- a macro for defining Clojure functions with pattern matching, Elixir-style. Still probably my favorite thing I've open sourced.
I find myself missing Clojure-style multimethods in most languages that aren't Clojure (or Erlang); once multimethods clicked for me, it seemed so blatantly obvious to me that it's the "correct" way to do modular programming that I get a little annoyed at languages that don't support it. And core.async is simply wonderful. There are lots of great concurrency libraries in lots of languages that give you CSP-style semantics (e.g. Tokio in Rust) but none of them have felt quite as natural to me as core.async.
I haven't had a chance to touch Clojure in serious capacity in awhile, though I have been tempted to see if I can get Claude to generate decent bindings for Vert.x to port over a personal project I've been doing in Java.
I had an idea about writing something similar, but for multimethods, but never got around thinking it through and trying it out.
The way defmulti and defmethod work is that they do a concurrency safe operation on a data structure, which is used to dispatch to the right method when you call the function.
My hunch is that it should be possible to do something similar by using core match. What I don't know is whether it's a good idea or a terrible one though. When you're already doing pattern matching, then you likely want to see everything in one place like with your library.
- syntax is hard to read unless you spend a lot time getting used to it
- convention for short var names makes it even harder
- function definition order makes it even harder
- too dynamic for most people's taste
- no type safety
- the opposite of boring
- no clear use case to show it clearly beating other languages
- niche with small community and job market
- JVM
For all those reasons its a hard sell for most imo.
We mostly hired people with no previous Clojure experience. Majority of hires could pick up and get productive quickly. People fresh out of college picked it up faster. I even had a case of employee transitioning careers to S.E., with no previous programming experience, and the language was a non issue.
I can't remember an instance where the language was a barrier to ship something. Due to reduced syntax surface and lack of exotic features, the very large codebase followed the same basic idioms. It was often easy to dive into any part of the codebase and contribute. Due to the focus on data structures and REPL, understanding the codebase was simply a process of running parts of a program, inspecting its state, making a change, and repeat. Following this process naturally lead to having a good test suite, and we would rely on that.
Running on the JVM is the opposite of a problem. Being able to leverage the extensive JVM ecosystem is an enormous advantage for any real business, and the runtime performance itself is top tier and always improving.
The only hurdle I could say I observed in practice was not having a lot of compile time guarantees, but since it was a large codebase anyway, static guarantees would only matter in a local context, and we had our own solution to check types against service boundaries, so in the end it would've been a small gain regardless.
I have to push back on this one, respectfully.
Clojure is easily the most boring, stable language ecosystem I’ve used. The core team is obsessed with the stability of the language, often to the detriment of other language values.
This attitude also exists among library authors to a significant degree. There is a lot of old Clojure code out there that just runs, with no tweaks needed regardless of language version.
Also, you have access to tons of battle tested Java libraries, and the JVM itself is super stable now.
I won’t comment on or argue with your other points, but Clojure has been stable and boring for more than a decade now, in my experience.
Have you worked for a company that hasn’t created its own, as you put it “mini language”?
Have you worked for a company that doesn’t indulge in over engineering, over abstraction and hidden cost?
Do you actually do programming for a job at all?
because programmers suck we should make tools that make it easier for them to suck?
Creating these mini DSLs is something that requires a lot of thought and good design. There is a danger here as you pointed out sharply.
But I have some caveats and counter examples:
I would say the danger is greater when using macros and far less dangerous when using data DSLs. The Clojure community has been moving towards the latter since a while.
There are some _very good_ examples of (data-) DSLs provided by libraries, such as hiccup (and derived libraries), reitit, malli, honeysql, core match, spec and the datalog flavor of Clojure come to mind immediately (there are more that I forget).
In many cases they can even improve performance, because they can optimize what you put into them behind the scenes.
https://news.ycombinator.com/item?id=47587386
There’s a rule of thumb: write a macro as a last resort.
It’s not hard to stick to it. In general, you can go a long, long way with HOFs, transducers, and standard macros before a hand-rolled macro would serve you better.
That’s pretty much exactly the opposite of how I always felt. Perhaps because I’m not a programmer by education, I always struggle to remember the syntax of programming languages, unless I’m working in them all the time. After I return to a language after working in other languages for a while, I always have difficulties remembering the syntax, and I spend some time feeling very frustrated.
Clojure and Lisps more generally are the exception. There is very little syntax, and therefore nothing to remember. I can pick it up and feel at home immediately, no matter how long I’ve been away from the language.
I agree with the short variable name convention, that's annoying and I wish people would stop that.
Everyone complains about a lack of type safety, but honestly I really just don't find that that is as much of an issue as people say it is. I dunno, I guess I feel like for the things I write in Clojure, type issues manifest pretty early and don't really affect production systems.
The clearest use-case I have for Clojure is how much easier it is to get correct concurrent software while still being able to use your Java libraries. The data structures being persistent gives you a lot of thread safety for free, but core.async can be a really nice way to wrangle together tasks, atoms are great for simple shared memory, and for complicated shared memory you have Haskell-style STM available. I don't remember the last time I had to reach for a raw mutex in Clojure.
Good concurrency constructs is actually how I found Clojure; I was looking for a competent port of Go-style concurrency on the JVM and I saw people raving about core.async, in addition to the lovely persistent maps, and immediately fell in love with the language.
Also, I really don't think the JVM is a downside; everyone hates on Java but the fact that you can still import any Java library means you're never blocked on language support. Additionally, if you're willing to use GraalVM, you can get native AOT executables that launch quickly (though you admittedly might need to do a bit of forward-declaration of reflection to get it working).
That's fair if you're looking at it from a performance perspective.
Not entirely fair if you look at it from a perspective of wanting fast feedback loops and correctness. In Clojure you get the former via the REPL workflow and the latter through various other means that in many cases go beyond what a typical type system provides.
> the opposite of boring
It's perhaps one of the most "boring in a good way" languages I ever used.
this is what i meant by that: https://news.ycombinator.com/item?id=47614353
While it's amazing once you've learned it, and you're slurp/barfing while making huge structural edits to your code, it's a tall order.
I used Clojure for a long time, but I can't go back to dynamic typing. I cringe at the amount of time I spent walking through code with paper and pencil to track things like what are the exact keyvals in the maps that can reach this function that are solved with, say, `User = Guest | LoggedIn` + `LogIn(Guest, Password) -> LoggedIn | LogInError`.
Though I'm glad it exists for the people who prefer it.
you absolutely do NOT need to learn paredit to write lisp, any modern vim/emacs/vscode plugin will just handle parentheses for you automatically.
that said, if you do learn paredit style workflow - nobody in any language in any ide will come even close to how quickly you can manipulate the codebase.
It's like seeing that a movie is playing at the theater so you show up only to sit down next to people to explain your qualms with it, lolz. Sometimes you need to let others enjoy the show.
The OP of this thread even said all that needed to be said "The learning curve is steep but very much worth it" yet we're trapped in this cycle because someone had to embellish it with a listicle.
I take my post back.
Plus to be fair we're having this discussion in the context of an article from 2021 that just rose to front page of HN, only to repeat the same set of pros we've been hearing about Clojure for ages (code as data, repl, etc).
Probably should expect some dissenting opinions.
it's not even in the top 50 here: https://www.tiobe.com/tiobe-index/. Lisp is 26.
For some reason I doubt this is in any way representative of the real world. Scratch, which is a teaching language for children, bigger than PHP? Which is smaller than Rust? Yeah, these are results you get when you look at the Internet, alright.
Besides, the community and ecosystem is large enough that there are multiple online spaces for you to get help, and personally I've been a "professional" (employed + freelancing) Clojure/Script developer for close to 7 years now, never had any issues finding new gigs or positions, also never had issues hiring for Clojure projects either.
Sometimes "big enough" is just that, big enough :)
Every problem people face is "not a problem" or "actually a good thing" or, maybe if all else fails we can make users feel bad about themselves. Clojure is intended for "well experienced, very smart developers". Don't you know, our community skews towards very senior developers! So if you don't like something, maybe the problem is just that you're not well experienced enough? Or, maybe what you work on is just too low-brow for our very smart community!
How about just "different"? Turtle want to teach everyone to program, that's fine, just another way of building and maintaining a language. Clojure is clearly not trying to cater to the "beginner programmer" crowd, and while you might see it as "unhealthy attitude", I'd personally much prefer to realize having many different languages for different people is way better than every language trying to do the same thing for the same people. Diversity in languages is a benefit in my eyes, rather than a bad thing.
Type's are for compilers ;) jk. I'm fully lover or type's but removing the constraint is easy in clojure. teams resist.
<3 the opposite of boring.
Not to mention in a post AI world, cost of code generation is cheap, so orgs even need even fewer devs, combine all this with commonly used languages and frameworks and you need not worry about - "too valuable to replace or fire".
Having said that - there may be a (very) small percentage of orgs which care about people, code crafting and quality and may look at Clojure as a good option.
This is only true if you assume C-like syntax is the "default."
But regardless of that, I'd argue that there's much less syntax to learn in LISPy languages. The core of it is really just one single syntactic concept.
Even among lisps this has been problematic, you can look at common lisp's LOOP macro as an attempt to squeeze more structural meaning into a non-S-expression format.
The syntax argument is such a tired argument. With LISPy language there is almost zero syntax, it's pretty much executable AST.
Because of this, formatting matters a lot, but I don't think that's too different than other languages.
If you think LISP is hard to read, you are someone who could most benefit from branching out to a non-Algol lineage language.
Also, the little syntax present is pretty much timeless. Learn once and its yours for the next 50 years.
To be pedantic, this isn't quite correct. Syntax isn't countable like that. What S-expressions are light on is production rules. At their most basic they have IIRC 7 production rules but there are absolutely 0 languages based on s-expressions which are that simple, since it doesn't give you anything like quasiquotes, vectors, Lisp 2 function resolution, etc. Reader macros make matters much worse.
What we can say is that they are constructively simple, but not particularly unique in that. Once you get into real sexpr languages they aren't simpler than horn clauses, and are constructively more complex than languages like Brainfuck and Forth.
It's repeated a lot because it's true. The collective developer world has decided that LISP syntax is not the preference. Good if you prefer it, but you're the in the overwhelming minority.
Random example i just found via github explore: https://github.com/replikativ/datahike/blob/main/src/datahik...
You probably love it but to me it looks like a wall of text. Sure I can figure out what it does, but it gives me a headache.
To use the right words: it’s not a syntax issue, it just looks unfamiliar to you.
To this day I have to look up whenever I get back into clojure what the "syntax" is of ns, require, import, etc.
but i bet if you sat down a junior developer not yet entrenched in any style yet, they'd be able to grok lisp code MUCH faster than the intricacies of syntax of the other alternatives ¯\_(ツ)_/¯
That is, prose is good for entertainment, but less so for conveying information, even less so for exactness.
I nominate this sentence for "best inadvertent freestyle rap on Hackernews".
The syntax is the best in the world (how computer's really operate?) but it's always been a pain to setup the tooling for me. I'm dumb like that. Now with AI it's become super easy to get back into the REPL and I'm in heaven.
Totally moving it back into workflow and proposing to bring it back into the dayjob.
I don't know exactly what you mean by this, but Clojure syntax is not really anywhere close to how computers actually process instructions.
Clojure is very nice though.
I love the idea of an AI integrated repl (like what Jeremy Howard and team have done with solveit), it's far more in line with my preferred vision of the AI augmented future of coding. Less "swarm of agent" more, "learn with the agent".
Setup is:
- nvim --listen /tmp/nvim — starts Neovim with a socket Claude can connect to
- /mcp in Claude Code — enables the Neovim MCP server, gives Claude direct control of Neovim
- lein repl in the nvim terminal
- Claude reads .nrepl-port, runs :ConjureConnect — REPL is live!
The loop is so dope:
- Claude writes code directly into my .clj files
- Then evals it into the running process via Conjure
- Sees the result in the REPL, iterates if wrong, all in the same conversation turn
- wrap-reload middleware means the web server hot-reloads changed namespaces on the next request
Feel like a holy grail to me to back in the hot seat with repl driven but I can drop in and figure things out... all on the JVM. Madness!
Something where I feel Kotlin did better.
For me the best way to introduce something like this is that I can actually start with small software increments on a Spring Java project.
no amount of ide smartness or agentic shenanigans is going to replace the feeling of having development process in sync with your thought process
https://github.com/BetterThanTomorrow/calva-backseat-driver
https://www.youtube.com/watch?v=98n32VstnpI
or toyota releasing a game engine: https://www.theverge.com/games/875995/toyota-fluorite-game-e...
(okay it's a design system, not so much a framework, but still)
We have a codebase at work that was "stuck." We've consistently done minor library upgrades, but no major upgrades in several years, and was recognized as a major piece of technical debt / minor disaster for almost two years, in that we urgently needed to dedicate an engineer to it for a month or more to bring it up to date. We also suspected that framework upgrades would improve performance enough to save us a little bit in operating costs. I got curious, created a branch, and threw Claude at it. Claude knocked it out in a couple of days while I mostly worked on other things. Then we dedicated several engineer days to doing extra manual testing. Done and deployed. Now we're ready to experiment with giving it less resources to see if the performance improvement holds up in practice.
This codebase was only about 200k lines of code, so probably smaller than yours. Really curious how it would go with a larger codebase.
EDIT: Claude may only have taken a couple of days because I was only checking in occasionally to give it further instructions. I don't know how fast it would have been with my complete attention.
and the benefit of lisp comes from the fact that every expression is fully delineated by parentheses, so you don't need to select any text, find the start or the end of any syntactic construction, you just eval the form you're standing at and see the result
or just as easily you can wrap that form, bind the locals to test values and eval that to see what changes
I think debuggers are just better anyways. When I got into the weeds trying to do this interactive REPL workflow on an actually running webapp it was a big mess. You have to write custom code all the time to basically capture data and save it into global variables, so that you can inspect them (as opposed to a debugger where you can just set a breakpoint and inspect all the variables in the middle of the precise spot in the request-response cycle).
I think maybe this was ahead of its time when it came out in 2008 or got popular in 2010-2015, but nowadays I am not seeing what's specifically more productive about the Clojure REPL than the interactive development experiences available in Laravel, Python, JS, etc.
If it was like a choice, Clojure has my favourite reverse debugger flowstorm which also exposes the runtime information of your program programmatically
So you can code cool visual programs against the runtime values of your first program
Like I built an emulator at work that simulates our program in production as a flowstorm plugin then you can step through the program frame by frame
I love taking the guess work out of production issues, just get the computer to show you exactly how everything went down
It's like a rewindable movie made up of thousands or millions of frames of the execution of your program
That's where "code is data" shines and you can manipulate it at will. For the rest, I'm glad I have "code as text" in my editor, where I can see and inspect it far more easily than one line at a time.
All that said, 90% of the time you still just eval a bit of a code to see what happens and that's the same between the two languages.
(you can theoretically pass "reload": true (or similar option) in launch.json for auto reload, tho I haven't felt the need to use that in my workflows.)
The cycle is:
Your feedback loop is now single digit seconds, without context switching. It's extremely relaxing compared to the alternatives (rerunning tests, launching the program with flags, what have you)."Typical REPL" workflow: Have one editor open, have one REPL open, have one terminal open that runs the application. One change is typically: Experiment in the REPL window, copy-paste into your editor, write tests, restart application (lose all state), setup reproduction state, test change. Or something like this.
In a Clojure REPL workflow, you'd do something like: Have one editor open, this starts the REPL and often the application in the background too. One change is typically: Edit code, evaluate that snippet of code (which sends it to the REPL and the running application), write tests, evaluate them too in the editor, if you're happy, hit CTRL+S and you're done. Application still has the existing state, no restarts needed and you essentially never have to leave the editor window/pane.
Of course, others might have slightly different workflows, but for myself and many (most?) other Clojure developers I've observed in the wild, this is pretty much the standard.
* Clojure is built different in terms of hot code reloading
* the REPL is its own application process in languages Ruby or Python, but in Clojure it’s sortof a client for the system
Is that right? Is there more to it?
So in a JavaScript server, you might have the database connection set in some config/thing you pass around to request handlers to use, and if you change a request handler, you typically stop the entire application, then start it again. Then the database connection (and the entire config in fact) would need to again connect.
In a Clojure application, first you either start the repl from the server, or start the server from the repl, then you might instead keep the database connection in an "atom" that you keep around for as long as you have the repl/server running. And instead of restarting the process when you change a handler, you'd only change the handler.
And yeah, the libraries and ecosystem at large is built with this (mostly) in mind for everything. The language also supports this by letting you "redefine" basically anything in a running Clojure application, which would be harder in other languages.
I've done some experiments years ago for doing the same in JavaScript, and it kind of works, but every library/framework in the ecosystem is not built with this in mind, so you'll be writing a lot of your own code. Which, now with LLMs, maybe is feasible, but can't say it was at the time exactly.
The fun way to get a feel for lisp machines is emacs, it's so easy to fall of a language and especially hand-coding in a language if you don't have to.
Personally, I find waiting more than 200ms unacceptable and really < 50ms is ideal. When the feedback is very small, it becomes practical to save the file on every keystroke and get nearly instantaneous results with every input char.
An example of me solving an Advent of Code with clojure and repl. You can see i never interact with the repl directly, I just send code to it via my editor and get results inline.
1. REPL is automatically compiled into running systems 2. Great hot-reloading support
So it's generally very easy to "poke" at a running system, and the whole dev process assumes you will do this.
TBH, these days it is largely possible in a C++ debugger. Less so 10 years ago, though.
there's a detailed explanation here: https://youtu.be/Djsg33AN7CU?t=659
https://clojure.org/news/2020/02/20/state-of-clojure-2020
The most recent report:
https://clojure.org/news/2026/02/18/state-of-clojure-2025
https://web.archive.org/web/20260402084152/https://blogit.mi...