The clojure.core implementation of "complement"

3 May 2010

Here is an interesting function from clojure.core - it's called `complement'. Let's see how it works:

user=> (even? 4)
true
user=> (even? 3)
false
user=> (def f (complement even?))
user=> (f 4)
false
user=> (f 3)
true
user=>

`complement' generates a new function which behaves very much like the original - except the fact that the new function returns false whenever the original function returns true and vice versa. How is this done?

`complement' is part of the Clojure `core' and its code is in the file `core.clj' (present under the folder src/clj/clojure). A great way to learn Clojure is to read the code in core.clj. Let's look at the implementation of `complement':

(defn complement
  "Takes a fn f and returns a fn that takes the same arguments as f,
  has the same effects, if any, and returns the opposite truth value."
  [f]  
  (fn  
    ([] (not (f)))
    ([x] (not (f x))) 
    ([x y] (not (f x y))) 
    ([x y & zs] (not (apply f x y zs)))))

Here are the things which you have to know to understand the code properly:

You can create anonymous functions using `fn'. Here is an example:

user=> (def f (fn [x] (* x x)))
user=> (f 2)
4
user=>

The keyword `fn' is used for creating anonymous functions - in the above case, we are binding the anonymous function to the name `f'.

A big use of anonymous function is to create what are called `closures':

(defn add [x]
   (fn [y] (+ x y)))

((add 2) 3) ; returns 5

Calling `add' with parameter 2 results in a function being generated on-the-fly; this function accepts a parameter `y' and returns 2 + y.

It's easy to define Clojure functions which accept variable number of arguments.

(defn fun [x y & z]
   z)

(fun 1 2 3 4 5) ; returns (3 4 5)

The first two parameters 1 and 2 are stored in `x' and `y' and all the remaining parameters are stored in a vector and bound to `z'.

Suppose you have a function which excpects 4 arguments - how do you call this function if all these 4 arguments are stored in a list? The solution is to use `apply':

(+ 1 2 3)
6
(apply + [1 2 3])

(note that in Clojure, the symbol `+' represents a function).

Finally, it's possible to define a Clojure function which performs different computations on the basis of the number of parameters it receives:

(defn foo 
   ([] 10)
   ([x] x)
   ([x y] (+ x y)))
(foo) ; returns 10
(foo 2); returns 2
(foo 1 2); returns 3

Keeping all these things in mind, go and have some fun decoding `complement'!

[Go to Code Clojure home] [Follow me on Twitter] [Go to pramode.net home]