Search code examples
programming-languagesfunctional-programmingclojurecomposition

What does it mean for something to "compose well"?


Many a times, I've come across statements of the form X does/doesn't compose well.

I can remember few instances that I've read recently :

  • Macros don't compose well (context: clojure)
  • Locks don't compose well (context: clojure)
  • Imperative programming doesn't compose well... etc.

I want to understand the implications of composability in terms of designing/reading/writing code ? Examples would be nice.


Solution

  • "Composing" functions basically just means sticking two or more functions together to make a big function that combines their functionality in a useful way. Essentially, you define a sequence of functions and pipe the results of each one into the next, finally giving the result of the whole process. Clojure provides the comp function to do this for you, you could do it by hand too.

    Functions that you can chain with other functions in creative ways are more useful in general than functions that you can only call in certain conditions. For example, if we didn't have the last function and only had the traditional Lisp list functions, we could easily define last as (def last (comp first reverse)). Look at that — we didn't even need to defn or mention any arguments, because we're just piping the result of one function into another. This would not work if, for example, reverse took the imperative route of modifying the sequence in-place. Macros are problematic as well because you can't pass them to functions like comp or apply.