If I declare a multimethod for another namespace (a library which I cannot change), ns-a, for my type:
defmethod ns-a/method-a Y [y]
and there's an existing method for X defined in ns-a:
defmethod method-a X [x]
and the dispatch function is
(defmulti method-a type)
if my type Y is also an X, how could I dispatch to the implementation for X in my implementation for Y ?
edit: I found a hack:
(defmethod ns-a/method-a Y [y]
(do-special-Y-stuff-here y)
; now do X stuff with Y:
((get-method ns-a/method-a X) y)
)
If you dispatching on type or class, consider revising your code to use Protocols.
More detailed answer:
Clojure's
defmulti
doesn't let you dispatch to a parent Java type if a precise subtype is also registered. This is intentional (it takes the side of "least suprise" in the Liskov substitution principle debate). Since there is already a registered multimethod for Y, if your objectisa?
exactly a Y, then you'll hit the method for Y - even though it's also an "X". In Java, classes can inherit from multiple interfaces, they can only ever be exactly one type. That's what you're bumping up against here.Per the documentation for multimethods
If you look in the source for
MultiFn
, you'll see that Clojure always uses the most specific Java class a given multimethod dispatch value (when dispatching on type).See this line in Clojure 1.4.0 source for
MultiFn
and specifically the implementation ofdominates
.At the REPL:
Ok, this works as expected, and
prefer-method
can't override the class hierarchy, becauseisa?
inspects the Java types first.Finally, in the source inspects
MultiFn
all the method keys that match the dispatch value type (as perisa?
). If it finds more than one match, it inspects the type hierarchy for the "dominate" value. We see here thatStack
dominatesRandomAccess
Now, if I define a new method
bar
as follows:I get the following, due to ambiguity:
Now, I can solve this problem with
prefer-method
However, if I register a new method for
Long
I can't get back to
Comparable
, even if I useprefer-method
:That seems to be what you've encountered here.
Note that you have the option of
remove-method
- but I think that is a much more heavy-weight / dangerous (monkey-patching?) solution compared to the "hack" you devised.