A lot of us Clojurians make the claim that programming in Clojure has provided us unique perspectives on computing. We further claim that these perspectives make us better programmers in other languages too.
this tweeter wanted concrete examples.
While I might not be able to make a case that it makes us better programmers, I'd like to provide a small catalog of example that illustrates the difference in perspectives.
Consider the problem of determining whether a given list is strictly in ascending order.
A Javascript programmer might write the following:
const isAscending = a => {
for (let i = 0; i < a.length - 1; i++) {
if (a[i + 1] <= a[i]) return false;
}
return true;
};
isAscending([1,2,3]); // true
isAscending([1,3,2]); // false
A Clojurian might write the following:
(defn ascending? [coll]
(apply <= coll))
(ascending? [1 2 3]) ;; true
(ascending? [1 3 2]) ;; false
Admittedly, the Clojure solution is quite clean. Syntactically and semantically. While the solutions are seemingly similar, there is actually quite a bit that is different. Even though this is a trivial example, it highlights a couple of major points.
- The cleanliness in the Clojure solution stems from
<=
being a function. While Javascript has anapply
, since<=
is an operator and not a function, the above solution can't be easily improved upon in JS. This extends to several other functions. An example of being able to do this in Javascript is withMath.max
. You can writeMath.max.apply(null,nums)
. But it is not something that Javascript developers reach for by default despite it being possible. apply
as an idiom teaches you that lists as inputs are not special. Again, if JS is your reference, then this is not an earth shattering revelation, but for someone who might work in a language like Java, this is a wildly different perspective.- We tend to avoid iteration in Clojure. This often helps avoid off by one errors and other bugs that iterating over indices brings. While JS does have a lot of iterative constructs, the popularity of a library like
lodash
makes it obvious that the language could use a few more. Again, there is no easy way of avoiding iteration here in a language like Java, even with the Streams API.
Overall, the idea that mathematical operations are functions should not be groundbreaking. The default perspective is that they are operators. The Clojure way is to think of them as functions. Again, I am not sure what is better but it certainly provides an entirely different perspective.
In Clojure, the previous problem can be written without a function body!
(def ascending? (partial apply <=))
A few other examples of functions without a body (a spiritual function perhaps? ;))
;; square of a num
(def square (comp (partial apply *) (partial repeat 2)))
;; check if palindrome
(def palindrome? (comp (partial apply =) (juxt seq reverse)))
;; check if prime?
(def prime? (comp (partial every? pos?)
(partial apply map mod)
(juxt repeat (partial range 2))))
(filter prime? (range 2 10)) ;; [2 3 5 7]
That last example is indeed rambunctious. A 4 line prime checker? Without a variable?! But what is the point of all this? It certainly isn't a good way of writing code. So what is the insight here?
The insight is that given the right primitives and an ability to glue these primitives together, a lot of code can simply be stitched together without having to write explicit definitions. Java most certainly wouldn't tease out this perspective, at least not easily. Even in the Javascript world, this perspective wouldn't be obviously gained.
How far would you get without being allowed to use a variable? None of the examples above use a variable, allowing the language to manage all of it. To be clear, I am not endorsing the above style. I am merely using it to illustrate the alternate perspective Clojure brings.
Here's a simple Fibonacci series.
(defn fibo
([] (fibo 0 1))
([a b] (lazy-seq (list* a (fibo b (+ a b))))))
;; first 10 fibonacci terms
(take 10 (fibo))
;; first 10 even fibonacci terms
(take 10 (filter even? (fibo)))
;; even terms among the first 10 fibonacci terms
(filter even? (take 10 (fibo)))
Thinking of a Fibonacci series as an infinite series is the right thing to do idiomatically. However, this is rarely done in most languages. While it can be achieved, since laziness is not a first class construct in most languages, it isn't tremendously useful.
The Fibonacci series itself can be written as an infinite series using a generator in Javascript. However, constructs like take
or filter
don't operate lazily. That limits the expressivity and rarely leads a JS developer to think in these terms.