I’m gonna make a bold claim here. As a language, Clojure is much better than Ruby. But, as a means of working together with other people, Ruby can be better, and Clojure can be a nightmare.
Here’s the problem with this debate: in Ruby it’s easier to actually bootstrap something because of the Rails framework. Clojure doesn’t have anything like that, but the code tends to get less worse as time goes by – as a functional language where everything is immutable by default, there’s less “surface” that’s touched by a piece of code. For example, suppose you have a function – in Clojure, changing that function only affects callers of that function, whereas in a mutable language, the function can mutate an object/value and now everywhere that uses that same object can be affected.
Another reason, that might be counter-intuitive is because of the real framework itself. When an application grows, you might find that you need less constraints and more freedom to do stuff that was not defined originally. Unfortunately, if you are using Rails (or any other framework), this means that you have to find ways around the framework, because it’s a kind of an “all or nothing” – it’s not that easy to basically do something outside of it and then bring that into your workflow, into your web servers and things like that (for example, I once had to integrate a SQS message queue on Rails, to listen to some message and deliver a notification via websocket. It was such a bad experience that we did a Node.JS app that did just that).
In contrast, in Clojure we don’t have a big framework; that means, most libraries are designed in a way that it’s easy to receive some info from some other library, and return the info for another library – it’s hard to see an example where this is not true (as I already mentioned in a previous post). It also have the full JVM at its disposal, which means that it integrates with Java objects, libraries, and basically have the world of enterprise software on its hands; this, together will well-thought and battle-tested integrations (JDBC, for example, is simply amazing and nothing really comes marginally close in most other languages) makes for a powerful, solid foundation, something that Ruby still struggles to do.
But here is the counterpoint – besides it being better, having more powerful macro capabilities, a way better REPL and interactive development support, when it comes to actual code experience in big groups and unknown codebases (like companies), it can mean that if you have some very complicated code, you can just “REPL-in” to probe and see what the code does. Again, this is not a bug, it’s a feature – but because we have this introspection capability (and powerful debuggers like FlowStorm, visualization helpers like Portal), it’s easier to probe internal states by yourself and inspect things, instead of, for example, displaying better errors, thinking about better APIs, or even write a better code (especially on edge-cases).
This is unfortunate, especially for me because, again in my opinion, Clojure is simply a better language – things work as expected, there are no surprises, bindings work with sane rules… compare that with Ruby, where self
is weird in some situations, bindings are complicated (some code define new scopes, some don’t, there are multiple ways to define classes, modules, and methods and they all behave in slightly different ways, etc), there are ways to call private methods (so there’s basically no isolation whatsoever, especially considering that private methods do get overridden by subclasses), no way to really run stuff in parallel because of the global interpreter lock (and the solutions to fix this are actually worse than simply spawning new processes), and it’s basically, a huge complicated language; even supposedly simple things like operation precedence is weird (do
-blocks have different precedence than {
-blocks, ||
and or
have different precedence, etc) and blocks have multiple different ways of being declared, and each way is different in slightly different ways (to this day I still don’t know the difference between proc
and lambda
, for example).
At the same time, Clojure’s power is both a blessing and a curse. In some code bases, macros are so pervasive that they basically change the way that the language works – I once worked in a code base that had a macro that would bound a variable to the return of each “line” of code, which gives some weird illusion to mutability. Another example is the clara-rules library – it’s an amazing rule engine, but at the same time, it rewrites code in such a way that tracing code, inline defs, and stacktraces simply stop working, so all possibility of debugging stuff is lost.
The same can be said for Electric, or Rama. These are amazing libraries, but honestly, look at Clara and Rama code and think: are they actually Clojure code? Or are they some “metalanguage” that shares the same structures (sometimes not even that) but essentially are different languages even with different evaluation rules and principles?
And that is where comes Ruby. Everybody that uses Ruby does more or less in the same way. Sure, sometimes it’s hard to write something from scratch because all libraries assume you’re using Rails or the public APIs of these libraries use some DSL that’s not composable; sure, it’s harder to write “raw SQL” because you need to “adapt” the SQL result to be “similar enough” to the ORM you’re using so that the framework doesn’t scream at you. Still, if you learn these techniques, you can apply then anywhere – comparing with Clojure, for example, where you enter a job, you have to spend weeks understanding their business logic, then months learning the “metalanguage” that’s used inside the job, so that you can be comfortable enough to start to implement your own abstractions in that metalanguage and evolve the system…
… and all that knowledge is gone when you leave the company. Nobody else uses the same “metalanguage”. You learned basically a self-made language that you won’t be able to reuse anywhere.
Where does the Simple made Easy idea went? Is this “Simple”? Or the simple idea of “simplicity” is already lost? Maybe it was never a thing to begin with?