Clojure productivity, readability
I really like the idea of Clojure and functional languages on the JVM in general, but I have noticed that I have a hard time being productive with Clojure. Although increased experience with languages usually increases productivity, it seems like there’s some details about its design and syntax that makes a lot of code permanently hard to read and write. I think there are a few reasons for this:
- The lack of both tail call optimization (so that one can naively write recursive functions like Scheme/Haskell) and mutable iterator update (C “for” style looping) makes writing many basic functions feel clunky. Not only does the “loop/recur” construct take a while to get used to, but it feels like the design of it adds indirection since the loop initialization, loop test and loop increment points are spread across three lines at different parts in the function.
- The use of braces (instead of whitespace) to denote blocks seems to amount to extra typing, noise on the screen and is usually redundant with indentation.
- The lambda syntax feels verbose “(fn [x] (x * 5))” (Clojure) vs. “\x -> x * 5” (Haskell) or even “(* 5)” (Haskell).
- Unlike Haskell “let” is not lazy in Clojure so one needs to nest let calls inside of branches and add extra braces/indentation. Many of my functions look like “let” soup
I wrote “fold” in a couple of different languages to explore this. Maybe there’s a “destructuring” shortcut to make the Clojure code more concise, but it seems like the “loop/recur” construct makes the corresponding Clojure loop code hard for me to comprehend due to the extra indirection.
Haskell
fold f i [] = i fold f i (h : t) = fold f (f i h) t
If Clojure had the tail call optimization
(defn fold
[f initial values]
(if (empty? values)
initial
(fold f (f initial (first values)) (rest values))))
Clojure
(defn fold
[f initial values]
(loop
[v initial
t values]
(if (empty? t)
v
(recur (f v (first t)) (rest t)))))
Python
def fold(f, initial, values):
for i in values:
initial = f(initial, i)
return initial
Objective-C (blocks)
id fold((id (^)(id left, id right)) f, id initial, id values) {
for (id i in values) {
initial = f(initial, i);
}
return initial;
}