It is unfortunate, really, that it’s 2021, and we still try to justify our personal preferences with “facts” that simply don’t exist.
I’m really sorry if this sounds aggressive, but as someone that saw this same pattern happening again and again, I’m quite tired. As Obie told us on a great talk about programming, music students, and painting, the pallette is not the point! Programming languages, frameworks, libraries, virtual machines are pallettes, tools, to make things better. As as with anything, people will prefer water paint, or crayons, or whatever – it simply does not matter. What matters is the ability to make great tools. Imagine if we treated music the same way we do with programming languages. It’s not hard to imagine bizarre conversations like:
- “What, you play piano? Why, how are you going to play on the streets? The streets is where you earn money, don’t you know?”
- “Wait, why are you learning guitar? How are you going to play on an orchestra?”
- “Ocean Drums? Why invent another percursion instrument?”
Sounds crazy, right? So, just read this tweet, and see the madness appearing. Maybe the way that Robert Martin posed the question was not the best way to convince people to try Clojure, but anyway, people jumped up on defense of their pallets, their tools, to the point it was quite tiring, really. I’ll not post who told what, and I’m not going to post the exact replies, but let’s debunk some myths:
Clojure is hard to read / we decided that code should be readable
This is the same as saying that “it’s easier to play the piano because every note you hit make a sound”. The thing is that there are things that are easier to write into one language, and others that are easier on others. Also, lots of the time, languages that are know to be obscure just need a little polish, or maybe people are just “unfamiliar” with the language. I mean, lots of friends of mine that know German say that these “incredibly long words” are easier to understand on the long run, because they are just “concatenations” of smaller words. But I don’t know a single word of German, so for me it’s incredibly unreadable. For example, let’s do a simple test in Haskell:
simpleMathTests :: TestTree simpleMathTests = testGroup "Simple Math Tests" [ testCase "Small Numbers" . simpleMathFunction 3 4 5 @?= 7 , testCase "Bigger Numbers" . simpleMathFunction 22 12 64 @?= 200 ]
Insanity, right? But what if we use another library?
simpleMathSpec :: Spec simpleMathSpec = describe "Tests of our simple math function" $ do context "when the numbers are small" $ it "Should match the our expected value" $ simpleMathFunction 3 4 5 `shouldBe` 7 context "when the numbers are big" $ it "Should match the our expected value" $ simpleMathFunction 22 12 64 `shouldBe` 200
Just by changing the test library, things are waaaay easier. But it's the same languages, "monads all the way down", etc… but it need you to re-think what you know. For example, you may think about Fibonacci sequences as "set the first number as 0, then 1, then accumulate then and calculate next by summing old and new". That's terrible performance-wise, so you may want to "cache" old numbers, and then make a loop… this obviously will be difficult to read in Clojure:
(defn fib-interactive [n] (loop [n1 0 n2 1 counter n] (if (<= counter 0) n1 (recur n2 (+ n1 n2) (dec counter)))))
But it becomes easier if you re-word the problem as “a list that starts with 0 and 1, and the next elements are the sum of the two previous elements”. This means, in Clojure, that’s an infinite list (lazy), and that you’ll sum the list itself with another version of it, removing the first element – so, you’ll have
[0 1 (+ 0 1) (+ 1 (+ 0 1))]. This becomes easy if you know the wheels:
(def fib (lazy-cat [0 1] (map + fib (rest fib))))
Also, Clojure have macros. You can write things like
(time-travel-to 1 day ago (do-my-code)), and they will be parsed correctly, syntactic analysed, etc (not the “string replace madness” that I had in Ruby, nor the “#define” madness in C/C++). And, just to close this argument: sometimes, your code will not be readable. That’s fine. Sometimes you’ll need performance, or the requirements are bizarre, or whatever…
Clojure have no static typing, so it’s worse
This is, personally for me, the worst argument to make. For various reasons – first that there’s no evidence, scientific or empirical, that points that this is true. There are some studies but nothing concrete. It’s also hard to prove, so people rely on their own personal experiences. So, let me just say the following:
First – Personal experience is not a valid argument
It’s completely valid to feel more safe, feel more confortable, with static typing. The keyword here is feel. When people say “oh, there are strict guidelines for some specific part of the software that mandates for static typing because it’s safer”, the thing they are saying really is “for some specific parts of the software people feel safer with static typing”. And that’s completely fine, really.
A point that lots of people don’t understand, is that there’s a difference between “knowing” and “believing”. Believing is not inherently bad – most people believe that the earth is round. Only when you do the experiments to prove that it’s not, or fly really high, you’ll know that the earth is indeed round. When people do academic papers, it’s impossible to know everything they need, so they use other papers for people that know (AKA – made experiments and proved that it was true) to build the knowledge that we need to prove a point ahead of the older papers. And again, that’s fine – but sometimes, people use papers that were disproved later, sometimes by mistake. It does not mean that their own conclusions will end up wrong, only that one of their base concepts is not right, so they may need to re-do the study from that point.
So, to conclude: I worked with multiple C#, Java, and Typescript projects that had lots of problems. They happened because people didn’t abstract correctly things, so making test cases, or even running locally the code was impossible, so production bugs happened A LOT in production. I’ve migrated some of these codes to Ruby and Clojure, and they worked fine. That’s not evidence that one language is better than the other
Second – The burden of proof falls over the one making the claim
It’s incredible how many the people that defend static-typing tell me that “I have to prove that I’m right” when they did claim that the systems are more secure. Yeah, static types will capture typing errors. It also makes harder for you to inject mocks in most cases. Also, most dynamic-typed languages allow you to change the behavior of a specific function in a specific context (like Clojure’s
alter-var-root for example) so that if you end up with a bug in some “untestable” code, you can easily fake some function to not hit a real API/Database/Complicated external resource and return a mocked version of data, so you can test that specific fragment. We even have libraries that will automate the process for you, so I’ll be more inclined to tell you that if you hit a bug on a dynamic-typed system, it’ll be easy to make a regression test for it.
But I can’t “prove it”, and I’m not going to try.
Third – Runs on JVM, and that’s bad/outdated/whatever
Seriously… the startup time of the JVM is nonsense, because you only open the REPL once, and re-start if you need to add dependencies. That’s like, 5 seconds, at most. Startup time is no-bother, so what about other issues? The mature library? The very mature VM? The ability to compile to native with GraalVM/native-image (in fact, SubstrateVM but that’s another history)? The ability to use other languages, again in GraalVM/Truffle?
People may not like the way Java apps are written, and I understand. But most Clojure libraries just need a thin wrapper around some Java, and that’s it. Most problems (“MySQL Server has gone away” error, “Support stream in ZIP / BZIP / Tar” feature, “Stream data from another source” problem) are solved on JVM libraries, and you can find almost anything on the ecosystem. Just for the record, I love doing things in ClojureScript (Node.JS) but the libraries are not half as mature, let alone on Ruby for example.
The Clojure/JVM is so great that you can run Python, Julia, and R inside Clojure with specific libraries, and to install anything you don’t really need to install a compiler toolkit or
lib*****-dev libraries on your machine, so I really don’t understand this point.
Seriously – I have lots of .NET programs (both classic and core) that refuse to compile because my SDK is a version higher or lower than what they expect, or because a NuGet package refuses to install, or whatever. On Clojure, you just add a specific version of a library by adding
[library "version"] on a file and that’s it… just download and install. So I don’t really see the point.
Should you abandon anything and write Clojure? Probably no. I decided to make this change, and it does conflict with other decisions I made for my life (for example, where I current live, Montevideo / Uruguay, I don’t know a single company that’s hiring and I know exactly ONE programmer that lives here), so it’s risky. Should you learn? Absolutely. There’s a lot to be told from the “decomplecting” and “de-abstracting” talks for Rich Hickey that do appear in Clojure. For example, it’s probably the language that I found easier to integrate other libraries, even ones that were not made to work with Clojure at all. It’s also a simple language, have a whole different way of writing code (see my Atom Chlorine GIFs to understand exactly what I meant), you can use the same language for frontend and backend (lots of Clojure libraries work on ClojureScript too, so you can even have the same code for validations in a single file that’s compiled for both targets), and you have “schemas” that will allow you to guard your code against “typing errors” and also “structure errors” like an empty vector, both on development and production if you want. Yes, it’s still runtime, but if you have tests, you can protect yourself against problems without having to “reorganize” the whole project just to be able to run tests.
So… let’s be honest: language wars are childish. If you disregard a programming language just because it doesn’t work the same as what you’re used to, you’re missing stuff, and you’re forbidding yourself to learn more about programming, or even problem-solving. If you go one step beyond and start inventing excuses and then try to present then as “facts” because they are “obvious”… I don’t even know what to say.