Thursday, April 17, 2014

A trip into Clojure, lesson learned

In my current job I had the chance to learn and use Clojure (this is the experience reported by a former collegue). Having worked with Python and Javascript I had consided myself well skilled in functional programming. But working with a more opinionated functional programming language was an eye opener. The benefits? robustness, better code reuse, a more organic workflow.

My main takeaway is that I can apply the lesson learned to Python and Javascript (and many others) as well. Let's see how.

Languages

Many languages enforce a specific point of view. Java for example with its pervasive object orientation enforce to model problems in objects. Python tries to please everyone with a solid OO implementation and a good deal of functional constructs. Javascript is similar (but with a different OO implementation).
Clojure, from this point of view, is more like Java: it enforces a specific way to model problems: a functional model.

Functions

Of course functions are first class citizen in a functional language so they can be stored in variables, passed as argument etc.
This allows a lot of useful programming patterns. These are just few examples:

  • Passing a callback: in event oriented programming (like events in browser or node.js) it's useful to pass a specific callback to run after an event
  • Decorator: you wrap a function in another function (useful for access control, memoization etc.). In Python there is a programming feature called "decorator" that makes easier to use this pattern.
  • Lazyness and partials: a function retuning not the actual result but another function that return the result. Useful to defer the actual task and if you need only a part of the result.

A brilliant example of what functional programming allows are wsgi middlewares (in the Python world) and Connect middlewares (in the node.js/Javascript world). As a matter of fact you can find a similar pattern in Clojure ring.

No objects - nothing enforce a local state

While the classic OO tries to encapsulate logic and state in the same entity (objects) a functional language keeps the two things well separated. This seems to be weird if you are used to OO but it has a lot of sense.
It is easy and more predictable to test the code because you are sure there is no mutation of the local state during the method calls. Any function has arguments and output: there is no side effect based on value of "this". Actually in Clojure you can have side effects (mutating atoms for example) but this is an exception, not the rule. And this is clearly explicit.

Types and common interface

Standard maps/lists offer a common and simple interface to a lot of generic functions. In the classic OO every object has a different interface requiring specific code to interact with.

In Javascript there are fantastic libraries like underscore, lodash to interact with standard arrays, objects (intended as maps in this case). You can also use hash maps and sets (Ecmascript 6). If you use  field descriptor you can access getters and setters using the usual semantic.

If you use Python, you should use more list an dict and less custom objects. Python can help you to keep the interface simpler using field descriptors and operators overloading. There are a lot of functional built in (like zip, map, filter, list comprehensions ...) and the fantastic itertools library.

(Im)Mutability

An immutable object cannot be changed. Any change creates a new object.
Mutable objects are a dangerous source of bugs. It's very easy to change an object passed as argument or by another function call without realizing that you are changing permanently the original object. This is a classic python gotcha.
It's not only a newbie's mistake: I have spotted the same nasty bug in Backbone.js.

In this case Clojure shines because everything is immutable by default.
If you use Python, you should never mutate an object/dictionary/list passed as argument. If you really need to do that, clone it before. Remember that list comprehensions, slice operator, and many built-ins return a brand new object and are safe. But it is better to check the doc because for example the "sort" method mutate the original list and the "sorted" built-in no.

The same advice is valid for Javascript too. Check the usual underscore and lodash for  clone and deepClone method. Remember that some array method returns a brand new array. This is a shallow clone but sometimes is good enough. A fantastic option is to use mori a library to use Clojure(script) data type and sequences in Javascript.

Lazyness

Working with sequences in Clojure is amazing because any operation is deferred. This is memory and computational efficient. This is not a new thing if you use Python generators, itertools or the new Javascript iterators and the for-of statement (ES 6). You can also use the Clojure sequence library with the aforementioned Mori library.

Development process

The functional approach (in my personal opinion) allows to develop in a more gradual way. While in the OO you have better results deciding the object model upfront, this is a bit complex in the long run. Some changes will require to move methods around the class hierarchy, create common base classes. And this kind of refactoring often invalidates a lot of unit tests too. So you often ends with small patches and code duplication.
The evolution of a functional program can be a bit chaotic but changes are more granular and it's easier to reuse code without doing a lot of impact.


This is the lesson learned: be more functional!

P.S. This is a nice book if you want to improve your Functional Javascript skills