DEV Community

Discussion on: 100 Languages Speedrun: Episode 21: Clojure

Collapse
 
hlship profile image
Howard M. Lewis Ship

I've been working professionally in Clojure since 2012; I think you are not being entirely fair in your cursory evaluation here.

In tiny examples such as above, you see the minor warts of Clojure without understanding any of the reasoning behind its design, or the benefits Clojure provides over both other Lisps or Java and other OO languages.

Clojure is walking a tightrope - it explicitly needs to interoperate with its host language, the JVM, but it is also aspires to the principle of least surprise. The fact that many, not all, languages conflate + as working for numeric addition and string concatenation should not be held against Clojure, which provides str for building strings.

You mention a few quality of life issues, such as underscores inside large numbers; but that is more syntax, and such things have a cost - for instance, searching your code base for where a 3 hour timeout (10800000 milliseconds) is defined is more complicated if the value could be represented in source as 10_800_000 or 1_0_8_0_0_0_0_0 or any variation thereof. A Clojure developer is more likely to write it as (* 1000 60 60 3) anyway.

I think it shows a lack of understanding of the problem domain to talk about Clojure's REPL as being bad based on a cursory view of how it handles keyboard input (you should use the clj command, not clojure, to get a more complete REPL experience, including the terminal handling you miss) -- but again, what Clojure's REPL does is fully integrated into the language to properly support incremental compilation. That means, that, from the REPL, you can re-define a function and have it take effect everywhere in your code base, or create new functions or namespaces - anything you can do in a Clojure source file.

I've seen other languages with fancier REPLs, but you have to restart quite often because only a small number of changes can be properly replicated to all modules (or namespaces, or whatever the nomenclature is). Clojure's compiler works on a form-by-form basis, so you get the same results typing into the REPL as loading from a file. There's no Clojure interpreter, just a Clojure compiler, either way.

I tend to start a REPL and use it for hours at a time, loading and re-loading code in a way that is not possible in most programming language -- typically, I only start a new REPL after switching branches and changing dependencies.

Lastly, an overview of Clojure that doesn't get into the power and freedom of its persistent (aka immutable) data types is short sighted. Our code base runs with hundreds of threads, executing simultaneously, sharing data richly, and we never have to worry about race conditions, locks, or data mutation bugs because of those persistent data types, and all the functional support around them.

What longer exposure to Clojure provides is an understanding of how all the parts of the language align, reinforcing each other, towards the goal of keeping you in the flow of development and producing concise, maintainable code. Working in Clojure doesn't feel like programming as much as it feels like a conversation between you and the computer.

I jokingly refer to Clojure as the "least worst" language - and the reality is I can get more done in Clojure, faster and easier, than in any prior language I've worked in.

Collapse
 
taw profile image
Tomasz Wegrzanowski

You're correct, I missed clj vs clojure. Somehow cursory googling mentioned lein repl (which I ended up using and stopped any further search), wrapping it in rlwrap manually, and a few really complicated solution, so I ended up missing this one. And I think I might have actually tried clj, but before installing rlwrap it doesn't actually work, and I didn't retry it after rlwrap clojure. But really - why doesn't it Just Work? Pretty much every other language with REPL just checks if stdin is terminal, and acts accordingly. Even Kotlin does that.

From the brief experience here, Clojure was constantly violating "the principle of least surprise". Obviously this isn't any deep dive, it cannot be while doing 100 languages in 100 days, and you can get used to all those introductory warts in any language.

My theory here is that any language with so many beginner warts will also most likely have issues at deeper level as well (like lack of tail call recursion). It's not scientifically provable, but it's been holding very well in my experience.

Some of the Clojure issues are due to JVM (like Unicode string length not working correctly - but then again, in JRuby it does). But most of the issues I ran into can't really be faulted on the JVM, they're Clojure-specific, so this excuse works only so much.

And in the end, I think it still ranks higher than most of the languages covered so far.