I've been working in Clojure now for about 12 years. Maybe 12+ years of Java prior to that.
I've created some great apps, and great libraries (in both Clojure and Java).
I often describe Clojure as "the least worst programming language", which is an off-handed complement, but I think accurate. Things you don't like can generally be fixed (at least locally) using macros and libraries. The core is strong, a good basis for building things ... and as described all over this thread, stable.
As you master your tools, you gain a level of speed and precision in your work that I have not found elsewhere. The REPL-oriented workflow is a central value proposition in Clojure, and many features (and a few limitations) of the language exist to properly support it.
Working in Clojure feels like I'm working "with" my code, molding it like clay. My prior experiences in Java and Objective-C were so much slower, with long code-compile-test-debug cycles that are compressed down to instantaneous responses in the running REPL.
My co-founder uses the phrase minimal-viable-company for maximum-viable-product.
We bootstrapped for 5 years to well over $1M+ ARR before recently closing a seed round[1], Clojure played a large part in our ability to deliver as a small team. Also in our general happiness as programmers, it is a nice language to work in.
We will grow our Clojure core product team over the next couple of years, but mostly the funding round is about balancing our business to keep up with our product delivery.
Clojure has been very good to me (I had 15 years on the JVM prior to moving to clj/cljs in 2013-ish). YMMV.
[1] https://factorhouse.io/blog/articles/from-bootstrap-to-black...
I love writing Clojure. Whenever I say that publicly, there are inevitably some voices challenging my stance with skepticism, criticism, and attempts to discredit whatever I say provides practical value for me. Then I have to explain to them, "no, it's not the only language I know," "yes, I've used dozens of other languages before," "yes, including languages with robust static type systems as well."
And you know what? Finally, I realized - I don't have to explain to anyone in exact detail why I have not found the same deep love in C, C#, Python, Javascript, Typescript, Ruby, Go, Java, Kotlin, Swift, Lua, Haskell, and many others. Why do I need anyone's permission to love a tool? I love it, and I love it for many reasons - theoretical, practical, emotional, fiscal.
Sure, I can get behind your excitement for Rust, Kotlin, OCaml, Elixir, Julia - you name it, but please, please do not try to "educate" me about my choices. I don't care about YOUR personal predicaments with Clojure/Clojurescript/Babashka/nbb, even Fennel. You find Clojure not to be worthy of your time - it's YOUR loss. My love for Clojure is not due to MY skill issues, not the result of MY inexperience, not because "I'm in a bubble," or "I don't know any better," or have zero knowledge of type systems, category theory, OOP or design patterns.
Sure, Clojure is not without deficiencies - no tool is ever perfect. Yet pragmatically, no other programming language in the category of general-use PLs today satisfies me more than Clojure, no other language is nearly as joyful to use. I'm sure at some point my stance will change, I will find some other "perfect" language for me, and 100% guaranteed - it will too have some deficiencies and people will be arguing for the merits of choosing it for the job. Until that day, let me just say it again - "I fucking love Clojure!"
The use of prejudice type behaviour towards others using a particular programming language based on hearsay or unknown is not ok, but also not something new.
The few folks I know using it are very happy with it, and are very experienced, and talented talented polyglots themselves.
For that reason I'd not rule it out, even if I'm not looking to make a jump or change, but I might try it out.
It’s great, and I learned a ton from Rich Hickey, despite not fully grokking Clojure or FP in general. I briefly worked at a small Clojure shop with an extremely talented crew. People were excited about FP and writing real business logic with it. My stack was different there.
The problem started when the honeymoon phase ended, and the codebase grew as the business gained traction. Dynamic typing became a burden, and once the key people moved on, they struggled to hire developers who wanted to write Clojure.
Also, JVM juju shooed many away. After I left, a coworker told me they had started rewriting part of it in Go, and that was going alright. Now, their stack mostly consists of Python for LLM stuff and Go for the main backend. There’s still some Clojure running legacy systems that haven’t yet been migrated over.
I’ve been having a lot of fun messing with clojure, but the dynamic typing really grates on me.
At your clojure job, we’re y’all using spec? I haven’t looked into it yet, and I’m wondering how much it mitigates the annoyances of dynamic types
I don't think spec with solve issues you are having about dynamic types.
Spec is about asserting structure at domain boundaries; once you're inside a context, it will not do anything.
i.e. If you're connecting stuff together and you want to make sure the state is correct as you pass between contexts -> great!
If you're writing stuff and internally you miss having a strong type system... uh... I don't think it's really going to help you.
It was in alpha stage and changing rapidly when they were considering it. But it’s similar to Python’s type hints and isn’t enforced by the compiler. However, that’s better than nothing.
In what way is clojure.spec similar to python's type hints? O_o
As a data point: I've been running my solo-founder SaaS business for 10 years now using Clojure. It changed my life. It would not have been possible without Clojure and ClojureScript, building and maintaining an app of this complexity would have exceeded my limits.
The article is excellent and I agree with everything in it.
The stability of the language is unbelievably useful. I look around and it seems it isn't valued in many other ecosystems where people have to rewrite their software regularly. I can't afford to rewrite my app.
There will be plenty of armchair critics here, with cliché knee-jerk reactions (parentheses, JVM, startup time, etc). If you intend to form an opinion, I would suggest you read only into the insightful posts, from people who actually used the language, or from critics who present well thought-out criticism, not just a shallow knee-jerk reaction.
I run a multi-million dollar business which I started with common lisp. I since moved away to go and then rust but I've been looking at clojure again lately.
For a team that needs to get s** done and has more per employee productivity than Faang combined it's hard to beat the speed with which you can build things when you have the repl and interactive programming.
The jvm while doesn't have great error messages is a fantastic runtime.
I feel like there’s a minimum developer quality that’s needed when using a language like Clojure, without static typing, and with a wealth of idioms and best practices that need to be followed and libraries that need to be known.
Without it it’s a matter of time before the codebase can’t be developed anymore and the software doesn’t work as intended.
I would love to see how Clojure's "wealth of idioms and best practices that need to be followed and libraries that need to be known" compare to almost any other popular language. Clojure is by far the simplest most straightforward language I've ever used with very few details and gotcha's that need to be memorized.
I can appreciate that. I'm on a journey this last year or so with Elixir, which also has an elevator pitch of "consistency" and "simplicity", true to a couple of defining principles, rather than hodge podge of committee like language initiatives.
But does it really matter now days? If you're doing anything web oriented, you don't get to abstract the hot mess that is web programming away. You still have to learn a ton of nuanced stacks, many couched in idioms that were established decades ago, and perservere today, wrapped in layers of whatever.
You could replace Clojure with Python or Perl, and this Would honestly be even more true.
well i agree you need developers who are on the senior side but thinking about data and pipelines, multi methods etc, makes for a quite extensible, large, applications.
the fact that because of the repl the happy path is always tested while being built and with clojure spec you can secure the edges, it is quite scalable.
i love static typing and rust in general but with web applications, the problem space is really a distributed concurrency problem of dynamic data. and clojure is basically tailor made for that.
Yes and actually this happens often that a project is moved to another language because of maintainability, especially when a key person leaves the company.
Did you move from CL because of hiring or another reason? I cannot imagine leaving CL and if I had to, I would go for abcl and then add clojure. Depending on the business that is of course. As long as I don't need to build a 1000s person tech team, I would not move; it's just far faster until you get to a scale where processes take over everything.
Hiring. And honestly the docs and stuff look really dated and I understand why people are a bit put off. At some point you have to modernize the look and feel of the ecosystem. The docs are really detailed though and if it was just me and a few others I would have gone to CL in a heartbeat.
Why did you move away from go? I don't use it, but seems like productive language, being simple, fast to compile and performant.
So go has runtime failures due to null pointers. When dealing with a bunch of json and dynamic data, I don't need to deal with null pointers in 2025.
You should give Gleam a shot. No REPL, but its simplicity, functional-ity and the BEAM lend themselves to the get-shit-done approach
Immutable data and REPL is where is at for iteration speed though.
Gleam takes more code to write than rust. Doesn't have a strong enough documentation / ecosystem yet. But I agree the beam VM is very underutilized and underappreciated.
> which you can build things when you have the repl and interactive programming.
This is how I use Python to be honest. If I'm unsure of something, I just bust out the REPL.
I know people talk about python being Lisp without brackets, but for example in SBCL and in other lisps like clojure I could just run a larger function, figure out that there is the wrong output, change a tiny function in it, eval that, eval the larger output, get the result. In SBCL there is also the break loop which is just magical.
I do not know how you do that in python without using a debugger and stepping through the code line by line. To be fair I used Python only a little bit almost a decade back. Are things more interactive now?
> I know people talk about python being Lisp without brackets
What on earth?? Who are these people, this is the first I've heard of this and am having trouble understanding what the argument could be. Like, basically all languages get transformed to an AST as part of the compile step soooo I guess any language is sort of a lisp.
The condition framework/break loop is one thing I wish more Lisps would copy.
Thats the one thing I miss in Clojure from CL. Although technically due to Clojure' immutability you have to do the same number of changes. But just dropping into the break loop, updating, continuing is just magic.
I think in Python REPL you just have to redefine the whole larger function. I am not aware of a way to access internally bound identifiers, or even modify what values they are bound to.
For starters, those identifiers aren't bound until the function actually runs.
Can you describe your stack and workflow ?
The part about the "stability" is a bit surprising - in my experience, I try playing with clojure about once a year, and every time, everything is different (I mean, I had to go through classpathes, then lein, then boot, then deps.edn - what is the current way to "try and run a program" du jour ?)
Also, is running your "hello world" still going to be incredibly slow, or has something changed in the core system (I know I'm supposed to fix that with graails. Or is it babashka ? Or something else, I suppose.)
It's really sad, because i just love the language. Reading about clojure is a pleasure. Trying to write anything has always been a blocker to me, though. Maybe that's the true "immutable" nature of the language ?
While clojure has gained (accreted) more options of ways to run things, every single one of the ways you mentioned still work! It’s extremely stable in this regard. Nothing was taken away because there is a new hotness. Sure, things were added. Why is that bad at all?
Personally, I prefer it when there is a solid default tool chain for a language rather than having to choose from one of several. That way, I know pretty much what to expect when I open any project in that language. I feel like this is one of the few places where Clojure dropped the ball and that deps.edn should've been there from the get-go.
What language ecosystem does this right?
Go
I have been building my (money-earning SaaS) app with leiningen for 10 years now. There is no "du jour", unless you choose to be "du jour".
If you care about startup time (I don't, my app starts only once), you can use babashka for scripting.
I've always heard that the JVM[0] in general is considered to have a fairly high startup time compared to other common compiled language targets; is Clojure especially bad in this regard, or is this something that's frustrating coming from outside the JVM ecosystem that just adds additional friction when you're already trying to look into using new language?
[0] Not sure what the correct term is here, but I mean the official default one; I know there are other implementations, and my vague recollection is that there's at least one other one that's either sponsored or maybe even fully developed by Oracle, and that the OpenJDK version is the "canonical" non-enterprise one now anyhow, so saying " first party" feels like it might add more ambiguity than it would resolve
~ > time bb -e '(println "Hello World")' Hello World bb -e '(println "Hello World")' 0.01s user 0.03s system 18% cpu 0.212 total
bb is Babashka: a pre-compiled (using Graal), interpreted, scripting language version of Clojure.
In terms of realistic work:
~/workspaces/github/pedestal/tests > time clj -X:test {:in user/eval2321, :line 13, :thread "main", :dev-mode? false}
Running tests in #{"test"}
...
Testing io.pedestal.test-test
Ran 391 tests containing 1180 assertions. 0 failures, 0 errors. clj -X:test 30.15s user 2.04s system 203% cpu 15.838 total ~/workspaces/github/pedestal/tests > java -version openjdk version "23.0.2" 2025-01-21 OpenJDK Runtime Environment Corretto-23.0.2.7.1 (build 23.0.2+7-FR) OpenJDK 64-Bit Server VM Corretto-23.0.2.7.1 (build 23.0.2+7-FR, mixed mode, sharing)
That's on my Intel MacBook Pro. Pedestal's test suite loads a good amount of Java classes and Clojure namespaces.
To make things faster, there's ahead-of-time compilation (which basically captures the read-eval-create-bytecode part of loading a Clojure namespace as Java .class files that can be packaged into your app).
The JVM has bad start up time in the context of milliseconds, but Clojures slow start up is in a different league. It loads a bunch of classes and it can take a full second.
But you're also not recompiling on code change. You're just reloading a function in the REPL. So you only have to load once and then your dev cycle is painless. But the startup time is bad for things like serverless functions unless you use something like GraalVM.
> everything is different… I had to go through classpathes, then lein, then boot, then deps.edn
All that stuff still works and I don’t think any of them broke their APIs? A lot of people still use lein for example.
You’re talking about new choices coming along, which is a good thing.
Of all the ones you listed, only one is a product of the core team, so it’s not like there has even been “change” on any official level.
If your mindset is, “I need to be on the latest hot thing” that’s about you, not Clojure. You’re allowed to keep building your projects using any of those tools, they still have the same capabilities.
10+ years ago Clojure had a fantastic introductory experience. lein new and away you go. lein was so good and effective, for both tiny hello world projects and real production apps.
The experience has gotten worse and worse now for a decade. The core team have continued to take things in a worse direction (supported by a small group of fanboys) and most newcomers are now completely baffled by the tooling.
The cli/deps.edn tooling is different from Leiningen, and far, far less complicated IMO. I've written a good number of Leiningen plugins and it was always brutal to get anything to work properly, especially in combination with other plugins.
Leiningen attempts to be everything to everyone in terms of building, testing, and packaging Clojure code. It's Clojure's version of Maven.
cli/deps.edn effectively reduces things down to a) what should be on the classpath and b) what should get executed. Working inside an IDE? You just want it to download the dependencies and build a classpath. Running nREPL? Add that to the classpath, and set the starting namespace to start nREPL. Packaging an application? Run dependencies that do that work, based on the clojure.tools.build library.
I was there myself; I used Leiningen and didn't take the time to figure out deps.edn --- until I did, saw the light, and converted all of Walmart's projects to use deps.edn, which greatly sped up our build and improved our dev experience.
I've worked around an open source Clojure codebase for a few years. I've grown to dislike Clojure by virtue of the culture it apparently fosters. As a community organizer in some vibrant decentralized spaces, reading articles from inside the community gives me ick feelings. There's a strange phenonemon involved when you have a diehard community that continually extols the virtues of the language, while the popularity of the language itself halves its open source marketshare every few years.
I mean, you have to try really hard to cultivate a community of people who have a deep love for something, but for some reason can't see what is actually wrong with it that drives ppl away. Passionate users are usually a boon, but apparently not for Clojure. But people somehow seem to build great companies with it. It's strange, and very specific to Clojure culture. I can't help but assume its failure to capture market is tightly tied to its leadership style and/or leader worship.
Anyhow, these are just hot-takes from the margins. I'm sure that passionate users will have something to say, or maybe downvote me.
I think there's something here. I'm not sure it's a bad thing. A negative framing might be that there's a sort of elitism. A more positive framing might be that it simply reflects a tension between different, valid views of how to build software which are sometimes (but not always) in tension.
After all, an elitist can also sometimes be right.
Disclosure: I say this as someone who has been about as deep as you can go in the Clojure community.
I started four years ago and in some ways still feel like a beginner. I have found the community super welcoming and positive - I’ve asked questions on r/Clojure, clojureverse, Ask Clojure and the Slack. A couple of my Slack and Ask Clojure questions were in retrospect poorly researched and I still got super helpful replies.
Did you encounter some hostility?
How to support the creation of beginners might be a different topic (and not a new one to technologies) to the offer of the technology itself.
How was your experience in working with the existing Clojure codebase?
Learning clojure has improved all of my programming in every language. I’m in love with the simplicity and smallness of it. Rich said one of the reasons he built it was he was programming like this already (pure functions acting on data) but the languages he was using didn’t support it well. There is a lot to be learned and applied even if you can’t use clojure at work every day.
Why Clojure = for Datomic, Rama, Electric and Missionary. No need for long blog post - this stack screams if your app fits within its intended operational margins - e.g. enterprise cloud information systems and rich interactive web products.
I tried clojure long time ago, honestly what made me give up is when I saw a java stacktrace in place of a proper error message when learning it. Also the repl was slow. The UX sucked, I wonder if they improved that
I used to hate JVM stacktraces but they’re incredibly useful and I miss them when I’m using other ecosystems these days. The way JVM exceptions do “caused by” when an exception is thrown while handling another exception gives so much more information than other languages. It’s also relatively simple to filter out the noise when you want a concise error message: just remove lines that start with a tab.
CIDER (the emacs IDE for Clojure) does a really good job of filtering the stack trace and presenting several views depending on what you care about. L
Usually there is also an error message together with a stack trace. I like stack traces: they are usually more informative than just an error message. I had lost too much time debugging Haskell exceptions where they by default do not come with any stack traces.
The only thing that really sucked is just the documentation. They are overly concise. You end up relying on non-official sources of documentation.
There are no Clojure stacktraces specifically, only JVM/Java ones. Clojure sits closely beside its host so there is no difference. People do complain about stacktraces and they've made some improvements. But they are long because the abstractions run deep and the function you passed in might be on the stack at the very bottom with the actual origination of the cause.
The repl isn't slow, what are you referring to?
Probably referring to startup time. Larger apps solve this with the “reloaded” type of workflow (https://www.cognitect.com/blog/2013/06/04/clojure-workflow-r...)
Elixir is a little less flexible since it doesn’t have the JVM interop but for domains where it’s a good fit I think it’s even better at most of this stuff (and easier to teach people unfamiliar with FP or lisps)
Idk ... it feels Clojure stalled since Nubank bought Hickeys company. Worst thing: spec is still in alpha !! I put so much on this ... but nothing really happened since ... idk 8 years?
Call it “stalled” if you like, it’s stable and it’s pure joy. I can just get stuff done with Clojure. And things that may seem inactive, like that lib that you need that hasn’t had a commit in 8 years, turns out that it just works and doesn’t need to change. This is commonplace in Clojure.
Spec is still alpha and I’m not sure it will evolve more or if it’ll be something completely different. At least they’re not pushing you down the wrong path. Use/look at Malli instead of spec.
There are things in the core language that are still kind of bullshit. The thing that gives me the most headaches is how annoying it is to use Java libraries that have the lambda syntax.
You can't just pass in a Clojure `fn` into a Java lambda function. You have to `reify` the interface and implement the single method. It's annoying and verbose and frustratingly the equivalent code is considerably cleaner in Java as a result.
I know that this is a product of how Java implemented lambdas by having interfaces with a single method, and I'm not saying that it would be trivial to add into Clojure, but I don't think it's impossible and I think people have been complaining about this for more than a decade now.
So while I love Clojure, it's probably my favorite language, I do get a little annoyed when people act like it's "stable" because there's nothing to fix.
I think that particular issue was addressed in the recent Clojure release [https://clojure.org/news/2024/09/05/clojure-1-12-0] ("Clojure developers can now invoke Java methods taking functional interfaces by passing functions with matching arity.")
Looks like you're right! I stand corrected, it's admittedly been a few months since I've touched Clojure.
You were not wrong though, Java 8 was released in 2014, so it only took them ~10 years ;)
Yeah, and most of my professional Clojure experience was from 2018 to 2021, and a few personal projects in 2022 and 2023, and that lack of support for the functional APIs really annoyed me; I was writing a Kafka Streams application, which uses lots of lambdas, and it annoyed me enough to rewrite it with vanilla Java.
I'm sure it's a difficult thing to implement, so I'm a little forgiving, but considering that Java interop is one of the biggest selling points for Clojure I do think it's fair to criticize a bad experience with it.
I need to play with the newer stuff though; the linked changes seem cool as hell.
I don't know what you're talking about, 2 new versions of Clojure have come out since then, both of which have more substantive changes than the previous couple versions.
Spec isn't really essential to Clojure, and most people use Malli nowadays which is very actively developed https://github.com/metosin/malli
I see the fact that there isn't much happening with the language as a positive myself. I want the core language to be small and stable. The big difference with Clojure and most languages is that it makes it far easier to implement new semantics in user space. It has a powerful macro system and new ideas can be expressed using libraries instead of having to be baked into the core language itself.
Most language grow through accretion as usage patterns change over time. New end up features end up getting bolted on to facilitate that, and the language keeps growing. The problem here is that the scope of things people have to know keeps growing as a result.
Meanwhile, Clojure managed to stay small and focused, while different kinds of ideas are expressed using libraries. When a new idea comes along people can just use a new library, and they don't have to learn about all the previous libraries that worked differently. I think this is a huge benefit from user perspective.
But have you seen flow? I don't think it's stalled at all, and Nubank seems to have become an enabler to the ecosystem.
I mean, Datomic is free to use!
[core.async.flow]: https://github.com/clojure/core.async/commit/03b97e0b3e0ec32...
Who are these articles for? I feel like Clojure’s value proposition is pretty well understood, and every last Clojure pitch can be relied upon to make the same exact points.
These articles are necessary to counteract armchair critics who are afraid of unfamiliar things and feel insecure. This crowd is loud: you can hear the choir of "but no one is using it, I tried it once and there were parentheses, I tried it and it was too hard, I hate the JVM" — there needs to be a voice that says it works (and works well!) for some people.
FWIW, it works for me — I would not have been able to build and run my business without Clojure. The long-term stability is an especially important trait, under-appreciated by people who do not have a business to run.
It’s probably geared toward someone like me. JVM and parens are one angle.
Another is that I get paid to work with distributed systems and databases, and Clojure isn’t even part of the discussion in that sphere. Go, Rust, and the usual Python and Node dominate there, so it’s hard for me to care.
However, I dislike language monoculture and am curious about why people like the things I might not care about. This blog is for those.
I’ve never used Scala or Clojure, but I heard them discussed a lot in the same circles in the late teens. It seems like Scala kinda vanished from common mention. Whatever happened to it, and what made Clojure take off?
I think the Java 8+ functional APIs/streaming/etc. kind of stole Scala's thunder. Java has all of the necessary tools to replicate the functional style of Scala now, and it's a lot easier to hire for, so :/
Using Clojure without Datomic can be frustrating since you feel like you lose some of the language's value, as you likely want to extend its philosophy and style to the database as well. Of course, Hickey probably knew this when he started planning Clojure, which is why he also created Datomic. However, Datomic can be a hard pill to swallow
Hard disagree: I never used Datomic and likely never will, and I don't feel like I'm "losing some of the language's value". Datomic is a database. You can use any database you like.
Datomic feels like a natural extension of Clojure for storing data—you get no impedance mismatch, you continue working with Clojure's data structures for storing, querying, and writing data instead of dealing with clunky query builders(or SQL strings), and you gain immutability. Sure, you can use any database you like, but then you're playing the same game as every other programming language, ultimately getting less value out of Clojure and making it feel like just another language—at least for applications that require a database.
I like Clojure well enough, but it feels like every single time I see it mentioned now is an write up trying to justify using it.
> justify using it
Justify using it against what? Can you name a single other language that does things better than Clojure? Not from your "point of values," but try seeing it from my position. Is there anything that can replace Clojure for me? I love the dynamism, malleability of the language - the flow; writing Clojure programs for me feels like playing a video game - it's plenty of fun. I don't get the same kicks from literally any other "mainstream-ish" language that's being used today. I like Common Lisp, but I have never had any practical, large opportunities to use it seriously, and there are things that I love in Clojure that I'm sure I'll miss in CL. I like Haskell, but it's even less practical (for me).
In my view, there isn't a single language that respects good ideas carried in Clojure and does them in a better or at least pragmatically viable fashion - I've evaluated Elixir, OCaml, Rust and some other choices. I am excited about Jank, can't wait for it to hit the first production-ready release. But today, nothing is more joyful for me to use than Clojure/Clojurescript.
Tried to love Clojure, but found it to opinionated as a daily driver for me.
Sometimes I just need to get some code running to see a result, which is my main use case for Lisp.
I used to default to Common Lisp, but it needs a lot of scaffolding to become ergonomic, and even then it has too many quirks to be really enjoyable for me.
So I started designing my own:
Macros being supported via quoted arguments is clever
Thank you, I'm very pleased with that detail :)
What wall did you hit? I can jump into a Clojure REPL without needing a project or even any files.
The opinions on data and programming in general-wall.
Not bad ideas, just not the kind I like shoved down my throat.
Arne has written, as usual, an excellent piece. Not only it argues its intended points, but it can also be followed by everybody-it brings readers up to speed without assuming any significant background knowledge.
Done me fine for the last decade. Along with all the other languages that kept me in employment. I still use it daily.
Now LISP has the big three. CL, Scheme and Clojure. Each introduces a fundamentally different way of thinking and all come from standards or standard implementations. There is also Newlisp, more akin to R and Julia and other LISPs each bringing some new things on the table. Still you need to have an idea from the big three to appreciate what LISP is all about.
Because you want the type safety of lisp with the simplicity of the jvm /s
You are being snarky but I'll take immutability over types and interoperability and access to a vast and stable ecosystem over the simple and extremely limited and underperforming alternatives. Thanks for the segway. I also cannot understate the reasoning ease that comes with basically everything being a static, top-level function. Testing never requires mocks because you can redef any function anywhere trivially when you otherwise would reach for a mock which tends to be painful, complicated, and requires comlex mocking libraries. Even with immutability by default, you get an extremely performant platform that usually stomps on nearly all the dynamic languages you might otherwise choose like Python or Ruby etc. And you get first-class functions and a concise and extremely stable language that is a joy to program in. Thanks Rich and Alex!
Immutability removes so much accidental complexity, it makes whole classes of mistakes dissapear. I'd also take immutability over types.
Clojure sort of guides you to simplicity, building everything out of functions and simple datastructures has big advantages when testing and reasoning about code.
I do find that in larger code bases, Clojure lack of types causes friction (spec is just a bandaid, not a fix).
There are languages with immutability and types (like Haskell), but these don't have the get-shit-done factor I seek.
> I'll take immutability over types and interoperability
OK.
> Thanks for the segway.
A segway is a motorized transport; a segue is a transition to a different topic. So this could be considered a type error, and demonstrates why you need types, not just immutability.
While amusing, I still disagree.
Complex static type syntax ends up infecting your code, because the syntax becomes load-bearing. A change to one type has a tendency to ripple through the entire body of code. This means you have tight coupling as a consequence of the syntax, which is a bad trade-off. Clojure's strong dynamic abstract types allow you to focus on the function of the code instead of its structure. Trying things out in the REPL first helps you find many of the things that you might need to wait for a compiler to find.
There are studies that estimate that the kind of bugs shipped to production that strong static types help prevent account for only 2% of defects. The rest are things like off-by-one errors, design errors, and incorrect logic.
The other thing a static type system can help you with is reading code. But it's primarily an aid to people unfamiliar with the language, or put in more popular parlance a "skills issue." We know tooling can navigate the code, because we have tooling that does so (language servers, CIDER, Calva, Cursive, Conjure...). This means that static typing for the purpose of merely reading the code is a crutch. I'd argue it's also a crutch or training wheels for writing correct code.
You can't run with crutches. You can't corner well with training wheels. Clojure is meant to make the expert nimble and fast. The main thrust of its design is to make experienced devs more productive, like professional tools without novice guide-rails make experienced craftsman more productive.
Rich Hickey gave a talk where he mentioned this idea of guardrails on the highway. He told a story of how we all get in our car, then bump into guardrails all the way to our destination... right? Of course not, we learn to keep the car in our lane of travel, to signal others when we switch, and to blend with other traffic on the way. (Rich was talking about TDD, but it still applies to static type systems, IMHO.) Clojure is that power tool without all the novice presets. It's the automobile we keep away from the rails when we drive it.
Mutable state is arguably responsible for far more defects shipped than type errors. Mutability is only a thing because hardware constraints required reuse of memory locations instead of allowing for persistent data structures. Thankfully, for "situated" applications that start and operate for extended periods, those constraints aren't so tight.
If you have to choose between immutable data and static types choose immutable data. But why not both? We can have all our luxuries, right? Because you have to spend so much more time proving things to the compiler, you have so much more syntax to manage. More code and more syntax for the same problem is almost always worse. I'll take Clojure.
> There are studies that estimate that the kind of bugs shipped to production that strong static types help prevent account for only 2% of defects. The rest are things like off-by-one errors, design errors, and incorrect logic.
If you've got a reference, I'd be interested. (Not saying you're wrong...)
> The other thing a static type system can help you with is reading code. But it's primarily an aid to people unfamiliar with the language, or put in more popular parlance a "skills issue."
It's an aid to people who are unfamiliar with the program, who have no idea what the shape of "that thing" is. (Especially, what is the shape of the thing that this function gets called with? For all possible callers?)
I've been hired to start working on code bases that have existed for a decade or two. Static, stated types are a life saver. Sure, they may slow you down while you're creating the code. It's easier to not have to explain everything that's in your brain. But when you and your brain leave, and I'm left with a bunch of variables that I have no idea what type they are - what are the maximum set of types that they can be for all paths through all callers - then I really need static types to help me figure out what's going on.
On the studies, I believe the results were drawn from this [1] study and followed up here [2]. I seem to recall reading a blog post based on these results, but I could be mistaken.
[1]: A Large-scale Study of Programming Languages and Code Quality in GitHub https://dl.acm.org/doi/10.1145/3126905
[2]: On the Impact of Programming Languages on Code Quality: A Reproduction Study https://dl.acm.org/doi/fullHtml/10.1145/3340571
Or you need a REPL that lets you poke at the living code. It's just different.
I think their point is, even with the REPL, it takes time to track down every spot that function might be called and figure out what was passed to it. A type is very easy to see localized on the function.
I've used Clojure for over a decade and the JVM has never been an issue. The only people I see complaining about the JVM are people who never actually worked with it. Meanwhile, there's zero evidence for the notion that static typing has any impact on code quality. https://danluu.com/empirical-pl/