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'!