#92: Clojure: a languages that will change the way you think about programming
Clojure is a dynamically, strongly typed programming language. It’s a dialect of Lisp running on the Java Virtual Machine. Lisp is 6 decades old and has a really weird syntax. That weird syntax is called Polish prefix notation. Basically, in every other language you’ve used math operators like plus or minus are infix. It means they are placed between operands. For example, 1 + 2
. In Clojure, you always put the operator (or any other function for that matter) in front. So simple addition becomes… + 1 2
.
This seems ridiculous, but such notation has many benefits. First of all, it’s much easier to parse. Essentially, the source code is already an abstract syntax tree. It’s also much easier to create source code from scratch, more on than later. Secondly, the language is very regular and consistent. Math operations look exactly the same as any other function. As a matter of fact, Clojure’s syntax can be described with just a few examples.
The language is further simplified by the lack of operator precedence.
I mean things like: multiplication is more important than addition.
Or: logical AND
takes precedence over logical OR
.
In Clojure, everything is explicitly controlled by parentheses.
No wonder why Lisp was jokingly abbreviated as Lots of Irritating Superfluous Parentheses.
OK, stop talking about syntax, Clojure is so much more. I consider metaprogramming the most important feature of this language. What is it? Well, Clojure source code is literally valid Clojure data structure. In other words, you can build a Clojure function that outputs some data structure. Typically a list of deeply nested lists called S-expressions. And that data structure can be a valid Clojure code!
I am not talking about returning a string with the source code. It’s more like having a JSON document that happens to be a valid JavaScript. But in the case of Clojure, any source code can be represented as such a data structure. OK, but what’s the point? Well, if you can implement a function that generates source code, you can just as well call that function during compilation, not at runtime! Such a function is called a macro. So in Clojure, it’s almost trivial to write code that generates even more code.
That’s the reason why Clojure language is fairly small. Many features that seem built-in are actually implemented on top of Clojure.
OK, it’s about time to discuss the industry application of Clojure. First of all, it embraces immutability and functional programming. This makes it great to express complex algorithmic problems and multithreading. One of the greatest concurrency primitives in Clojure is Software Transactional Memory. Basically, you can surround a piece of code with a software transaction. Clojure makes sure that all mutated state is either modified entirely and atomically. Or not at all. Just like database transactions, but without persistence.
There’s also a concept of agents. These objects encapsulate mutable state. But the only way to interact with them is by sending functions which are queued and run sequentially. In a way, it’s a simplified actor framework.
Clojure is actually much more than that. It supports runtime polymorphism, can compile to JavaScript, and interacts nicely with the Java ecosystem. Even if you don’t intend to use it commercially, it’s one of these languages that are worth learning to broaden your horizons.
That’s it, thanks for listening, bye!
More materials
- Clojure on Wikipedia
- Lisp on Wikipedia
- Polish notation on Wikipedia
- #59: How compilers work: from source to execution
- Refs and Transactions
- Operator precedence
- https://github.com/razum2um/awesome-clojure