Multimethods aren’t magic (nor mutable)

mistakes — cgrand, 28 May 2008 @ 17 h 55 min

What does this code display?

(defmulti foo first)
(def print-foo (comp println foo))
(defmethod foo :a [x]
  "it's a 'a'!")
(print-foo [:a])

Nothing, it raises an error: it doesn’t know what to do with the dispatch value :a. The value of foo is captured when comp is called.

The thing to keep in mind is that defmethod mutates the var, not the multimethod itself — which is of course immutable. The solution is to pass the var instead of the multimethod to comp:

(defmulti foo first)
(def print-foo (comp println #'foo))
(defmethod foo :a [x]
  "it's a 'a'!")
(print-foo [:a]) ; prints "it's a 'a'!"

Update or maybe it’s a code smell that you should use a macro instead of a function.

Update 2 Clojure’s father (Rich Hickey) says : the correct thing to do is use the var.

1 Comment »

  1. You are right – the correct thing to do is use the var – #’foo. This is a specific case of the more general problem of building long-term connections to function objects. Saying (map foo …) is fine, as foo is unlikely to change (nor would you want to see it change) during the execution of map. But when you ‘capture’ the value of a var containing a fn (as the call to comp does here), and store it away for use later, you probably want the var itself instead, as it will reflect redefinitions and bug fixes. Macros are not the answer here.

    Comment by Rich Hickey — 31 May 2008 @ 15 h 39 min

RSS feed for comments on this post. TrackBack URI

Leave a comment

(c) 2024 Clojure and me | powered by WordPress with Barecity