Konrad Garus
Konrad Garus

Reputation: 54005

Clojure multimethod on class OR keyword

Suppose I have this multimethod

(defmulti m (fn [v] [(:type v)]))

(defmethod m [Object] [k] (prn "Object"))

(defmethod m [:mykwd] [k] (prn "mykwd"))

When I call it with subclass of Object, it correctly dispatches to the first implementation:

(m {:type String})
"Object"

With the :mykwd it also works as expected:

(m {:type :mykwd})
"mykwd"

But when I provide another keyword I get an exception:

(m {:type :anotherkwd})
#<CompilerException java.lang.IllegalArgumentException: No method in multimethod 'm'
for dispatch value: [:anotherkwd] (NO_SOURCE_FILE:0)>

How exactly does this dispatch work?

Is it possible to preserve this behavior for class inheritance and still have a "default" implementation that catches all keywords?

EDIT This example is simplistic, but I need it to work with binary functions. My real need is below. I don't see how I could apply :default to it.

(defmulti m (fn [arg mp] [(class arg) (:type mp)]))

Then I'm looking for a way to define it for the case when arg is nil and (:type mp) is anything. This works when value for :type is a class, but not for any keyword:

(defmethod m [nil Object] [arg mp] (prn "Whatever"))

Upvotes: 0

Views: 1552

Answers (2)

Alessandra Sierra
Alessandra Sierra

Reputation: 10887

Try replacing your :type dispatch with a custom function that returns a default value when :type is nil. Same for class. Then you can dispatch on a vector of keywords. Add the keywords to a hierarchy if you need inherited behavior.

Upvotes: 2

Thierry Lam
Thierry Lam

Reputation: 46264

There is a default:

(defmethod m :default [x] :oops)

Reference:

Check http://clojure.org/multimethods at the bottom of the page.

Upvotes: 5

Related Questions