Multimethods aren’t magic (nor mutable)
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
.
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.