top | item 25622528

Why Clojure? (2018)

185 points| bribri | 5 years ago |briansunter.com | reply

216 comments

order
[+] thom|5 years ago|reply
Every time Clojure comes up on HN there always seems to be a lot of negativity. I've come to flinch just seeing a mention of the language, which I do still love and have used for a decade, but perhaps this is how everyone feels about their pet language. I think there's a bit of a tension with Clojure, that it's a weird combination of being principled and being pragmatic, which means that there are always languages that on paper beat it out on any given axis. At the same time Clojure people do have a tendency to paper over the cracks quite a lot, at least some of which must be cognitive dissonance.

The biggest problem is that it's very hard to picture why, at the end of the day, all the choices that went into Clojure come together into a productive whole for building real-world software. It's a really nice mix of terseness without preventing clarity, simple lightweight modelling paradigms, interactive development, easy access to multiple cores, and all on top of the JVM with its enormous ecosystem. It's not as Lispy as other Lisps. It's not as pure as other functional languages. It doesn't have a fancy type system. It doesn't have native performance. But it gets stuff done and it does so fairly elegantly in most cases. I've found it a really solid career choice, there's really very little that you can't solve in a satisfying way. Plus whatever you think about parentheses, Clojure syntax basically hasn't changed in 10 years, most new features are just libraries of new functions and macros, and for me that validates that it's the correct approach.

[+] mumblemumble|5 years ago|reply
I've never been a huge fan of lispy syntax, but I've never been a big hater, either. Personally, I find the square brackets and curly braces to be just enough additional texture to make the code plenty readable, as long as you're using a good autoformatter. (Letting people make their own decisions about code formatting in any lisp seems to be a very quick route to a very bad time.) What I find, though, which makes me sad, is that many people knee-jerk reject any non-ALGOL syntax (Lisp, but also Smalltalk, ML, Prolog, whatever). To the point that I fear that not being able to understand the syntax becomes a point of pride. It reminds me a bit of the sort of asymmetrical intelligibility of Metropolitan French and any of the Canadian dialects, a situation where I also get the impression that the difficulty in understanding is more than a little bit deliberate.

So there's that. But I also do find that Clojurists have not been immune to some of the snootiness that seems to infect most functional language communities. And I wonder if some of the reaction comes from that, combined with Clojure maybe bumping into the rest of the world more often due to its identity as a JVM language.

[+] Kalium|5 years ago|reply
> At the same time Clojure people do have a tendency to paper over the cracks quite a lot, at least some of which must be cognitive dissonance.

An executive I previously worked with commented one day "Every small language's following is a little bit cult-y. Clojure's is more so than most."

Personally, I think the technology itself is fine. The ecosystem, from my exposure to it, has a few warts. It's somewhat small, which I expect of what is still a niche language. The killer issue for me was the memes that slowed and discouraged the development of useful frameworks in favor of gluing together ad-hoc sets of libraries.

Of course, it may have just been that I was working with a batch of Clojure devs who were in those jobs because they were sold on the glories of functional programming purity. Hard to say. They were better than the Haskell shop I worked in, but not as much so as I would have liked.

[+] cageface|5 years ago|reply
The problem with lisp is that most programmers find the syntax extremely off putting. I've been watching this debate go around and around in circles for 20 years now and nothing has changed. And as much as I appreciate the power of macros I feel like the features of other modern languages cover a lot of the same ground so the benefits of such a controversial syntax are diminished.

Lisp is a fascinating and very elegant language and every developer can learn something from it but I gave up waiting for the lisp revolution a while ago.

[+] city41|5 years ago|reply
To not go absolutely insane with Lisps, you need some kind of parentheses tool with your editor such as ParEdit. The up side is once you get used to your tool, you can do really cool things and navigate/change code in higher level ways. But the massive downside (in my experience), is convincing coworkers to adopt a Lisp is practically impossible. The language itself being so foreign, and when they ask about the parentheses bringing up a tool they need to learn on top of the language is a double whammy.
[+] mnming|5 years ago|reply
I am a long time Clojure user (since 2012). I am still using it in my personal web projects with a lot enjoyment.

However, I wouldn't use it on any projects with more than maybe 3 team members for mainly three reasons:

- Very steep learning curve despite the seemly simple syntax.

- The community is shrinking with many high profile OSS projects being effectively abandoned, while most other languages' communities are growing.

- Clojure is just bringing too much freedom for most average developers.

If it's just me, I will have no hesitation and always pick Clojure as my main language.

[+] mbrodersen|5 years ago|reply
Thanks for being levelheaded in your assessment of Clojure. I am so tired of claims that language X is “better” based on a subjective selection of what “better” means. X might indeed be better for Y (that you care about) but not for Z (that I care about).
[+] johnday|5 years ago|reply
Now I'm somewhat biased, but that complete list of advantages also applies to Haskell. [^1]

In fact, nearly all of the claims made about Clojure here can be made about haskell more strongly.

I've half a mind to do a direct comparison on every point. I'd be interested to hear the author's thoughts on the similarities and differences.

[^1] With the obvious exceptions of s-expressions, java/js interop, and "subjectively good design".

[+] teodorlu|5 years ago|reply
I really appreciate both Clojure and Haskell. I agree that

> nearly all of the claims made about Clojure here can be made about haskell

, but I'm not sure about `more strongly´.

## DX with Haskell (and ML friends) I miss in Clojure:

- Harder to do sound, up-front design with data types and module interface, where implementation becomes almost trivial after when the type signatures make sense

- Consistency checks from compiler / type checker

## DX with Clojure I miss in Elm/Haskell:

- REPL ergonomics, where every action I could think of makes sense as a REPL command, leading to very small incremental pieces.

- Excellent default data structures with literal representation and serialization (EDN)

I'd love to read a point-by-point discussion of the article sections comparing Haskell and Clojure, though that's much to ask for in the comments field.

[+] cdmckay|5 years ago|reply
The JVM interop is a huge positive for Clojure, in my opinion.

Being able to consume any JVM library makes Clojure usable in many more professional settings than Haskell.

[+] vnorilo|5 years ago|reply
I actually don't want to get into the argument over which is better, but dynamic and static types do change everything, also with regard to how those features in the list feel in a language.
[+] fulafel|5 years ago|reply
Compared to Haskell, Clojure, like Elixir and Erlang and Scheme, is a much smaller and simpler language and is easier to learn especially for people without a theoretical CS / math background. This is due to Haskell's ambitious and powerful static type system.
[+] didibus|5 years ago|reply
Interop, REPL driven development, s-expressions, and great support for working with maps seem to be the ones that don't apply to Haskell from my quick glance.
[+] macmac|5 years ago|reply
I would agree with all of these points except "pattern matching". Yes several libraries implement it, but it is not built in and even the best libraries feel clunky compared to for instance the built in destructuring. Rich explicitly rejected pattern matching ala ML for the reasons provided here: https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd6...
[+] bmitc|5 years ago|reply
I wish he gave more examples in that response because I'm generally confused what he's talking about. I'm familiar with Racket and F#, but not having used Clojure, I'm missing some context about the Clojure ways of doing things and examples of the problems he claims.

> I feel about them the way I do about switch statements - they're brittle and inextensible.

That is not the case in a language like F# or OCaml. I do note that F# was introduced only slightly before Clojure was, but pattern matching provides nicely extensible functions and are anything but brittle in those languages. Also, active patterns in F# allow one to extend the pattern matching functionality.

> The binding aspect is used to rename structure components because the language throws the names away, presuming the types are enough. Again, redundantly, all over the app, with plenty of opportunity for error.

I'm not sure what he means here. Again in a language like F#, names of the data aren't thrown away. They are pattern matched against, only being "thrown away" to do actual calculations. Nothing is ever lost where the data came from. For example:

    type Shape =
        | Circle r
        | Square s

    let area shape =
        match shape with
        | Circle r -> System.Math.PI * r * r
        | Square s -> s * s
There's no confusion here. In fact, pattern matching in a language like F# allows one to completely remove the possibility of error. For example, this really shows off in parsing applications. Once your parsing function returns a type that can be pattern matched, it's extremely difficult to have an error in the pattern matching sections of code. These are typically the most robust parts of the application.

> I'd much rather use (defrecord Color [r g b a]) and end up with maps with named parts than do color of

    real*real*real*real,
> and have to rename the parts in patterns matches everywhere (was it rgba or argb?)

I don't understand this either. In F#:

    type Color = { R: float; G: float; B: float }

    let colorFunction { R=r; G=g; B=b } = r * g * b
No names are thrown away. Also, the comment on rather using maps seems to assume the data type for every element of the data structure is the same. How do you just use maps when the underlying types of your record aren't the same?
[+] fiddlerwoaroof|5 years ago|reply
I always found core.match really nice: but, in general, I strongly prefer using multimethods for the sort of thing other languages use pattern-based dispatch for.
[+] nojito|5 years ago|reply
Calling pattern matching brittle is absolutely hilarious and shows how out of touch he is.

F#'s pattern matching is a clear example of it done correct.

[+] BossingAround|5 years ago|reply
How about Clojure vs Scala? Anecdotally speaking, I've seen more Clojure than Scala at my company, both being incredibly niche (I've seen more Groovy than either to be honest).

If I want to get more into FP, is there any strong positives/negatives for either? I must say though that after using Racket for a bit, I am a fan of the parens. Makes expressions crystal clear.

[+] lxtx|5 years ago|reply
Language intricacies aside, is there a reason to use Clojure over Elixir, Erlang? Genuinely curious what JVM has to offer vs BEAM / OTP if you're going to use dynamic languages.
[+] bernardv|5 years ago|reply
This laundry list of features still does not tell me why I should be using clojure over any other language.
[+] lbj|5 years ago|reply
Well, thats true but its implied.

The entire syntax for Clojure fits in a single line. Its easier to learn and being as expressive as it is, the core idioms are quickly picked up as well.

So - You can get into it quickly, very quickly if you're already familiar with FP.

The brevity of the code means that you'll produce much more robust code, which takes up a lot less screen real-estate. This allows you to grasp the functionality of any code you read, very quickly and start working on the problem.

It'll go as fast as Java, but slower than C/Rust. For some performance oriented tasks, you'll have to put in more work than makes sense, to get the performance you want. But for 99% of the Apps that are being written, Clojure will perform just fine and you'll end up with better code.

Compared to Haskell or most other FPs (not F#) you get the added benefit of being on the JVM. Write once, run everywhere. Huge libs to do everything from 3d graphics to webserving.

In most cases, I use Clojure for the above reasons summed up in this one sentence: I makes me more effective than the alternative.

ps: Having enjoyed Lisps for 20 years or so, Ive never used Par-Edit :)

[+] didibus|5 years ago|reply
I use Clojure because I find it more more fun and interesting to program in. As a bonus, it happens to also be practical, robust, productive and safe; with great tooling, a huge ecosystem, reach to the browser, server, command line, and desktop/mobile, good performance, good scale, and an awesome community.
[+] tkdc926|5 years ago|reply
> In Clojure, whenever you "append" to a vector (array) you get a "new" vector and the original does not change. Anyone with a reference to the original can always count on it being the same.

This has never made any sense to me. Can someone please explain why you would still want the original vector to continue to exist with data that no longer reflects the current system? What am I missing?

[+] fmakunbound|5 years ago|reply
I was using Clojure for a while before 2018, and the error messages were brutal. Has that improved since then?
[+] darksaints|5 years ago|reply
I always think it's hilarious when Clojure enthusiasts try to address concerns about the language by talking about parentheses, as if that was actually the major barrier to entry. The parentheses are at best a mild inconvenience...many people love them, including myself, but few people cite the parentheses as a reason to not use the language after actually trying it out. A non-exhaustive list on why not clojure:

* It's slow

* Development with the REPL is slow because the startup times are glacial and REPL-oriented development usually requires tons of from-scratch restarts.

* The tooling sucks: build systems, IDEs, debuggers, etc. If you feel like writing code with just an editor and a terminal is like going back to the stone ages, you're gonna want to bash your own head in with a mammoth bone club.

* Java interop is a black art, and when you need to use it, it will ruin any sense of elegance you felt for your code originally.

* The ecosystem practically doesn't exist, unless you're willing to absorb a lot of java libraries. See above.

* The lack of static types hurts you in many ways, most of all your ability to refactor with confidence.

* Clojurescript isn't the same language, no matter what they promise you. Clojurescript is weakly typed, Clojure is strongly typed. If you aren't aware of the difference, prepare to spend weeks of your life tracking down bugs that would only be days in Clojure, and would never exist in the first place in a statically/strongly typed language.

[+] raspasov|5 years ago|reply
* It's fast enough for 99% of apps out of the box. It's fast enough for 99.99% of the apps with minimal tuning.

* Yes, if your project is very big and macro heavy, it can take some time, but startup times have improved. In any case, I BARELY need to restart my development JVM. I have one currently running that I haven't restarted for 1 week+.

* Depending on what's your cup of tea, there's emacs/CIDER or IntelliJ/Cursive. They both work well. IntelliJ/Cursive is an excellent IDE combination. I use it every day.

* Java interop is very straightforward, not sure what you mean. Sure your code might not be all pure anymore, but that's the price for solving actual problems.

* Good java libraries have wrappers. A ton of original Clojure libraries as well. https://github.com/cgrand/xforms for example allows you to easily do things that I can't even imagine doing in an imperative language.

* Static vs dynamic typing: don't want to get into that.

* "Clojurescript isn't the same language". I use both Clojure and ClojureScript every day and as far as Clojure-only code is concerned, it works in both languages 99.99% of the time. One case you can encounter issues is if you do something host-specific, like dealing with numbers. That's by design. Clojure embraces each host, does not try to reinvent it. When you just use pure Clojure data structure manipulation, it works the same across both languages and works like magic.

[+] EastLondonCoder|5 years ago|reply
Here are my observations after doing clojure for a bit more than a year coming from doing js for two decades.

It’s fast, both clj and cljs but performance is non deterministic and as with most functional languages it can be hard to reason about performance. Profiling cljs is very hard

Coming from the js world I feel that tooling generally works. Especially when it comes to build systems. Very happy to not deal with webpack inconsistencies. With regards to ides there’s cursive for IntelliJ users, Calva for vscode.

I can’t comment that much on Java interop. Js interop is a mild inconvenience.

With regards to ecosystem, google ability is an issue but tempered by the simplicity of both clj and cljs.

About types, there are a class of bugs they will help to avoid. It does help with understanding intent of the code you work with. The drawback is that they help facilitate abstraction and does not help with reasoning that much about a program actually runs.

Yes cljs is a different language. But in practice it feels very much the same. I do however have a big issue with being able to push deterministic performance out of it. Keeping execution time for any frame below 16ms can be a challenge and for some type of front end stuff js is to be preferred

All of these things are quite insignificant on how fast a team can churn out good quality code using clojure. I’ve never seen any team that I’m with at the moment write correct, readable and fast code as quick.

[+] dimitar|5 years ago|reply
While everyone is entitled to their own opinion, I think it is worthwhile to give more detailed examples - like for example slow compared to what?

For example there is a nice discussion over slow startup (note that for the most people the impression that it is slow comes from the Leiningen startup which starts two JVM processes): https://stuartsierra.com/2019/12/21/clojure-start-time-in-20...

[+] dgb23|5 years ago|reply
Clojure is fast or similar in comparison to main stream dynamic languages.

REPL development is what I miss so much from other languages. You typically don’t add dependencies as frequently for startup being an issue.

[+] codespin|5 years ago|reply
Tooling, ecosystem, and Java are what killed clojure for me. I love the language and the way Rich Hickey approaches it, but I was constantly frustrated with the lack of an IDE and having to stop to deal with Java and Java tooling errors all the time.

In the end I felt like Clojure was too clever for its own good. By relying on Java (which was a great choice) it took a lot of the oxygen out of the ecosystem for other devs to build tooling and libraries, and without that there aren't as many people participating or becoming a well known community member from their work there. Again, can't say this was the wrong choice, but in my opinion ecosystem is the #1 thing for a language and the way Clojure was done had an impact on how the ecosystem could grow.

[+] jgalt212|5 years ago|reply
> REPL-oriented development usually requires tons of from-scratch restarts.

I heard that. I've read Clojure developers keeping the same REPL running for days and avoiding this problem, but I'd always be worried about the state of the global namespace not being what I thought it was. And this is especially true during development / exploration.

[+] kolme|5 years ago|reply
I'm going to have to disagree with almost everything you said, let's have a look:

> I always think it's hilarious when Clojure enthusiasts try to address concerns about the language by talking about parentheses, as if that was actually the major barrier to entry.

Not that they are a major barrier, but they do scare people away. I know this because I've been tempted to try lisps before and after having a look at some source code, I was "scared" of the parenthesis. They are ugly (at least I thought at first sight) and they seem like very tedious to type. Of course I know better now, but that was _the_ reason I had not to try lispy things, at least on a couple of occasions. So yes, the "the parens are A-OK!" is absolutely warranted when trying to sell Clojure or any lisp for that matter.

> It's slow

So what? If I'm not writing anything CPU bound, I'm not very concerned about that. Clojure is fast enough for most things. The performance penalty is completely justified if it takes me half the time to write an app. This is just a trade off, or do you write all your stuff in ASM?

> Development with the REPL is slow because the startup times are glacial and REPL-oriented development usually requires tons of from-scratch restarts.

This is just very much not true. First of all, nobody is forcing you REPL-oriented programming. Second of all, it doesn't take more than a couple of seconds for most apps to restart. And third, if you use "def" and "defonce" correctly you'll hardly ever have to restart the REPL.

> The tooling sucks.

That's just your opinion, man, and it's also pretty rude. Clojure have awesome IDEs and tooling, and the aforementioned REPL-development allows you to have the same "react-hot-relading" developer experience, anywhere.

> Java interop is a black art.

Java (and Javascript) interop is pretty straight forward. You call functions and instantiate classes and stuff. No naked dancing under the moon involved.

> The ecosystem practically doesn't exist.

It maybe didn't some time ago. The ecosystem right now is thriving with pretty cool projects.

> The lack of static types hurts you in many ways, most of all your ability to refactor with confidence.

That's also pretty subjective, in my opinion the only thing that gives you confidence to refactor is unit testing.

Plus there's other ways to validate parameter and return values in Clojure, and they are not limited to the type.

You'll agree, that a return value is of a given type doesn't guarantee that it's correct.

> Clojurescript is weakly typed, Clojure is strongly typed.

Plain wrong, types are semantically equal in both languages (they are weak). The semantics of the target languages are just implementation details.

You can try this on a REPL of any variant you like:

  (def foo 42)
  (def foo "Hello, World!")
Sharing code between Clojure and Clojurescript is fine, especially if you stick to the built-in data types and functions. Of course if you have a lot of specific interops interleaved, it's going to be painful.
[+] Scarbutt|5 years ago|reply
To expand:

Compared to JS, yes, it's slow. You can make Clojure run faster but you will be writing Java with parenthesis not Clojure.

Startup times are indeed atrocious, in Clojure the REPL is obligatory and not optional because of this. You can avoid REPL restarts by using a third party lib like component but you have to buy into a new architecture for your program that can be overkill for many occasions.

Java interop is not a black art but it is painful, the JVM has many great libs but the majority are not, many are loaded with lots of methods returning void, data models like everything needs to a be subclass of an abstract class, etc... So while you can develop with time how to write ad-how Clojure wrappers for your use case faster, you waste lots of time doing it. And yes, this something you have to continually di cause as you said, the ecosystem is very poor.

Without going into the static vs dynamic typing debate, I would say Clojure is at the top of the dynamic languages spectrum (the best in its class if the JVM fits nicely the problem at hand).

Clojurescript's interop with the JS ecosystem is crippled by relying on the closure compiler, which doesn't play well with anything in the JS ecosystem.

[+] sukh|5 years ago|reply
The article and examples were lucid and easy to read. Thank you!
[+] non-e-moose|5 years ago|reply
I'm not sure I see the benefits here unless you are buying completely in to: Emacs, and Java and ignoring performance/overhead. Closure is implemented in Java; and the only apparent way to write it is via Emacs. The zero-eth issue I see: functional programming espouses absolutely no side effects, meaning no capability of handling network of physical I/O errors The implementation being in Java means that it is not possible to use it in embedded environments (which might not be a problem for some users) but it does mean that performance is JVM limited. Personally, I find the requirement of Emacs to be QUITE odious. In my opinion, Emacs is an OS/environment and not an editor. I'll use it if I want to edit a binary, but when an editor includes a psychotherapist mode (Eliza) it is not suitable for software development. I have also seen some quite talented researchers/engineers spend multiple seconds trying to remember the sequence to do X in Emacs, when it would have taken FAR less time in vi or vim. Too many parentheses make the code UNMAINTAINABLE in a production environment.
[+] pinchhit|5 years ago|reply
A couple of things here:

- Intellij IDEA with the Cursive extension is very popular outside of emacs (I've met more clojure developers who use IDEs than those who don't.)

- Clojure uses the error handling mechanisms of the target runtime. You have try/catch statements and side effects are often used. It's not a no-side-effect language.

- Parentheses are almost always managed with parinfer/paredit and python-style indentation rules in production code I've seen.

You're quite right that performance will be tied to the JVM or V8/SpiderMonkey/etc.

[+] newtwilly|5 years ago|reply
There are other IDEs people like besides emacs.
[+] x87678r|5 years ago|reply
Has anyone seen a big - multi year system done in FP? Lots of people love FP, it seems great for your own side project, but I'm not convinced it works in those typical big corporate systems where devs turn over every few years as the code base grows.
[+] JackMorgan|5 years ago|reply
I've worked in big F# projects in banking. It's absolutely superior to C# or Java in many ways. One notable drawback is "how much code can new hires write in their first month" which is not a metric I consider that important for big enough projects. The month or so needed to skill up a C# or Java programmer in F# is a drop in the bucket compared to the benefits it brought us.
[+] beders|5 years ago|reply
It's working fine for us and we just hired 6 new clojure devs. Code base is quite significant yet easy to make meaningful changes to.

Most reasoning is local to your changes thanks to the focus on small, pure fns dealing with immutable data.

[+] gidan|5 years ago|reply
Pitch (from Berlin) is using Clojure as well as Reagent with React. From what I saw it's quite a moderately sized app (not a gigantic one, but not a side-project sized either, in between that's what I mean).
[+] swayvil|5 years ago|reply
Gratuitous parenthesis nesting. We see it a lot in Clojure.

Nesting boxes instead. It could be represented, in the editor, that way.

It could be much more legible.

Is anybody doing that?

[+] jopython|5 years ago|reply
Most of the clojure libraries i found were suffering from bit rot. Looks like the community is shrinking.
[+] draw_down|5 years ago|reply
I no longer think language matters much. I like some more than others but they’re all a bag of hurt, you just get to pick which kind of hurt. I agree that pure functions are nice. But things like this end up mattering a lot less day-to-day than dumb-guy practical stuff like packaging and tooling. The important thing about clojure is that it runs on the jvm.