(4 weekends, to be precise)

“A programmer should learn at least one new programming language each year.”

Clojure is a functional programming language. I never particularly liked the term. In discussions related to functional programming, it always feels like a bunch of nerds talking a weird language that has nothing to do with real life: monads, laziness… All the toy stuffs that highlight the intellectual superiority of the cult. Most importantly, I want readability. Code is the means to express yourself to the machine and peer programmers. The more expressive and intuitive it is, the better. This is why I liked Python over C++ and Java so much. My friends did Scheme before, and it just seems like a luxurious brain exercise, while I have plenty of other things to spend my brain on.

The main reason I tried Clojure was the way it handles concurrency. At work I’ve seen a couple of enterprise softwares that let people drag and drop components into a diagram that will do data processing. It’s easy to convert those diagrams into serial code, but not the other way round. It’s partly why people cannot simply have a compiler that translates serial codes into programs with concurrency. We need a programming language that is also expressive of the dependencies of the tasks in a program – the intent of the programmer.

From this perspective, syntax is the first thing that strikes me. I don’t like the prefix order, as it’s unfamiliar, but it’s not too bad as it’s very quick to learn. Except for the mathematical formula. Turns out, it allows for a lot of flexibility. You can even write a code to write codes. You can also visualize codes as diagrams.

Looping is the next thing that strikes me. I cannot write loops the way I did with C, Java or Python. Instead I needed to use tail-recursion to replicate this. I avoided using loops, as it doesn’t work well with the way my brain was reasoning about functions. Instead, map and a bunch of other functions that operate on collections are preferred. Usually this means cleaner code, and sometimes codes that can be parallelized. It’s embarrassingly hard to write this kind of code compared to the old ways of simply looping, as you are now trying to communicate your intention & organization, not only your instructions.

Along with looping was immutable persistent data structures. It makes it harder to write codes, and the code is less efficient. Clojure did a great job reducing the inefficiency to a constant factor with that “persistent” property. With these data structures, now one can really talk about functional programming, as data is now value, and a pure function will always give the same output from the same input. Memoization can easily be supported as a result (a plus for me since I have to implement dynamic programming algorithms now and then).

Because you now communicate with the compiler in terms of values and functions rather than instructions, it is easier to visualize your code. You can write the code on one side of the screen, while the other side automatically update the values obtained by the function (kudos to LightTable). At the same time, you tend to use smaller functions, because the program becomes visually ugly as it grows pass a certain size. The combined effect is a new programming style, where you write small functions, unit test it instantly by typing in different values and observe how the end result is updated on the output screen. Small functions can be called to composed into other functions naturally, as you don’t need to worry about side effects.

Image

After a few days familiarizing myself with Clojure, the old Python code now looks ugly to me. My old way of writing code was clearly harder to test, and the dependencies between code blocks weren’t clear.

It was one way Clojure changed my thinking about code. It’s not always desirable, for example when you want to write some quick dirty patchwork. But for a long term project, I believe that laying down those code organisation, and reasoning in terms of functions and values, are always things you should strike for. Not that it’s always possible though, and I like that Clojure allows for flexibility built into its design that lets you leave the functional world graciously.

Another great idea in Clojure is about abstraction. It provides a bunch of tools to generate new classes and new objects with different properties. This took me another few days to get a hang on how Clojure does OOP. It’s like you have different kinds of loops for different intentions (foreach loop, map-reduce loop, iteration loop), you also have different sorts of inheritance and polymorphism for different intentions. You can add functions to old types, and you can also add new types that support old functions. Sometimes you can just get away with a new instance, rather than having to create a new class/interface. A legacy class/object now becomes something alive that can be broken down, combined, or taught new tricks.

If Perl’s way is “there are different ways to do things”, Python’s way is “there is one prefered way to do things”, then Clojure’s way is probably “there is an ideal way and a practical way to do things, and we’ll tell you when you need to make a choice”.

I haven’t discussed the most prominent use of Clojure – how it handles concurrency. I haven’t mastered it now, but it is definitely a good topic for another time.
———————————-

Now I’ll discuss how the implementation details are affected when you port an algorithm to Clojure. We are finding all the primes under a threshold.
https://github.com/richhickey/clojure-contrib/blob/78ee9b3e64c5ac6082fb223fc79292175e8e4f0c/src/main/clojure/clojure/contrib/lazy_seqs.clj#L66

Notice the main body of the code:

(primes-from 11 wheel))

It takes a function primes-from, starts from 11, and use the wheel, which is an infinite sequence as to instruct the steps it should take to find the next prime.
Primes-from perform a primality test as follows:

(some #(zero? (rem n %))
(take-while #(<= (* % %) n) primes))

… where #(zero? (rem n %)) is the test of division, and (take-while #(<= (* % %) n) primes)) is the list of potential factors.

A typical implementation of this algorithm in non-functional programming language would be the Sieve of Eratosthenes:

for i in range(n):
    if isPrime[i]:
        j = i
        while j<n:
              isPrime[j] = False
              j+=i

I believe the latter code is faster to write and to run. However, its meaning is only clear if one has a rough idea of the algorithm it’s trying to implement. The former code can be broken down into independent pieces, and the reader can try to understand each piece before combining them altogether. Of course, you can also implement the latter code in Clojure, just that it doesn’t give the natural feel. When you leave the functional programming realm, you know right away.

Advertisements