Archive

Archive for the ‘functional programming’ Category

Clojure multimethods

January 6, 2011 5 comments

I have just began my adventures in Clojure land. So far it seems exotic for someone who’s just beginning to grasp the marvels of functional programming and for whom Lisp has previously been just magic inside parentheses. However, even though my experience with Clojure is very limited, I’ve already noticed that it is a very enabling language.

I’m planning to post brief introductions or tutorials on Clojure’s features on this blog, mostly to educate myself. I’m sure I won’t be able to avoid errors. If you spot one, please let me know. I’m also very interested to learn what is and what isn’t idiomatic in Clojure. I hope my Clojure code doesn’t turn into Fortran. 😉

One of the interesting features of Clojure (and some other languages) is multimethods. They are basically Clojure’s way of implementing runtime polymorphism. A multimethod is a method that dispatches a call to a specific function (method) implementation  based on some property of its argument(s).

Example 1:

(defmulti animal (fn [x] x))
(defmethod animal "dog" [x] "Woof!")
(defmethod animal "lolcat" [x] "I can has cheezburger")
(defmethod animal :default [x] "Unknown animal")

Running in REPL:
user=>(animal "dog")
"Woof!"

user=>(animal "lolcat")
"I can has cheezburger"

user=>(animal "giraffe")
"Unknown animal"

user=>(animal ["some" "unrelated" "stuff"])
"Unknown animal"

Here (fn [x] x) is the dispatching function which dispatches the call to the correct function implementation. In other words, it evaluates its argument(s) and produces a dispatching value which is used for finding  the actual implementation (defmethod). In this example, a simple string is used as an argument. It evaluates to itself and nothing more. The third defmethod is the default implementation which is called if none of the other defmethods match the dispatching value.

Example 2:

(defmulti thoughts-of (fn [person] (person :name)))

(defmethod thoughts-of "Guido" [person]
    (str (person :name) " has lost his shrubbery."))

(defmethod thoughts-of "Rich" [person]
    (str (person :name) " values laziness."))

(defmethod thoughts-of nil [person] "--")

(defmethod thoughts-of :default [person]
    (str (person :name) " is not a well-known thinker."))

Running in REPL:

user=>(thoughts-of {:name "Guido"})
"Guido has lost his shrubbery."

user=>(thoughts-of {:name "Rich"})
"Rich values laziness."

user=>(thoughts-of {:name "Tero"})
"Tero is not a well-known thinker."

user=>(thoughts-of {})
"--"

Here a hash-map is used as an argument and the value of the dispatch method is the the value found from the map using the keyword :name as a key. In the example the map itself is used as a function which takes the key as an argument.

Alternative ways for writing the same dispatching function would be:

(fn [person] (:name person))
Keyword as a function in the body of the dispatching function.

(fn [person] (get person :name))
Using the function get. This is perhaps the most conventional way.

:name
Using the keyword alone as the dispatching function. This is effectively the same as the first alternative but with less boilerplate.

Multiple dispatch mechanism is not unique to Clojure. Eg. as far as I know Python doesn’t natively support it but provides several ways for achieving it. In languages like Java and C++, multiple dispatch is difficult to implement.

For further reading, see:

Wikipedia article on multiple dispatch contains examples in other programming languages.

More theory and examples:
Clojure’s Approach to Polymorphism: Method Dispatch

Advertisements