DEV Community

Brandon Correa
Brandon Correa

Posted on

Clojure Composers: Introduction

Over the next week or so, I'd like to talk about Clojure's function composition functions in a series of small and focused posts: Clojure Composers.

Today, I'll just discuss some composer basics.

About

Function composers provide us moulds with which we may create other functions. If we were to put this in mathematical terms, a function composer is a function f(x) that returns g(y).

This is great for us because rather than writing something like this...

(map #(some complicated (code %)) stuff)
Enter fullscreen mode Exit fullscreen mode

...we can have a composer that looks like this...

(defn complicator [f]
  (fn [thing] (some complicated (f thing))))
Enter fullscreen mode Exit fullscreen mode

...then write this!

(map (compliator code) stuff)
Enter fullscreen mode Exit fullscreen mode

Giving us a clean way of hiding the ugly stuff behind the stuff we care about.

Anatomy

There are a couple characteristics that apply to all composers, and I'll use Clojure's simplest composer, constantly, as an example.

(defn constantly [x]
  (fn [& args] x))
Enter fullscreen mode Exit fullscreen mode

Parameters

A composer will take one or more arguments, and use them in some way to build out a function. In the case above, constantly accepts only one parameter. However, other composers such as juxt or comp can take any number of arguments.

If a composer takes zero arguments, then it should either be a constant or it depends on something externally from the function.

Return Value

We can assume the return value will always be a function, which may have any arity (usually consistent with some defined pattern). Zero, one, two, infinite... In the example above, constantly always returns a function that accepts any number of arguments.

Conclusion

While we can use composers just about anywhere, I find that they really shine in functions like map, filter, or sort-by where there is usually already some chaining of functions happening.

Which of these do you find easier to read?

(map #(-> % :kills :confirmed) kittens)
(filter #(-> % :kills :confirmed zero?) kittens)
(sort-by (fn [{:keys [age weight]}] [age weight]) kittens)
Enter fullscreen mode Exit fullscreen mode
(map (comp :confirmed :kills) kittens)
(filter (comp zero? :confirmed :kills) kittens)
(sort-by (juxt :age :weight) kittens)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)