Clojure, from this point of view, is more like Java: it enforces a specific way to model problems: a functional model.
FunctionsOf 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.
No objects - nothing enforce a local stateWhile 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 interfaceStandard 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.
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)MutabilityAn 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.
Development processThe 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!