1

I am taking an example from the clojure site.

(defmulti foo class)
(defmethod foo ::collection [c] :a-collection)
(defmethod foo String [s] :a-string)

(foo [])
:a-collection

(foo (java.util.HashMap.))
:a-collection

(foo "bar")
:a-string

This functionality is cool. I'm trying to understand the technical reasons why this is superior to an instanceof based implementation in (for example) java. It seems to me essentially equivalent in function with nicer syntax.

public <T> String foo(final T t) {
    if (t instanceof Collection) {
        return "a-collection";
    } else if (t instanceof String) {
        return "a-string";
    } else {
        throw new IllegalArgumentException("Undefined - no available dispatch");
    }
}

What are the reasons why multimethods are considered a great alternative to visitor pattern based double dispatch while instanceof is not when they seem like they're essentially doing the same thing?

Daniel
  • 546
  • 1
  • 6
  • 15
  • here's an example for different dispatch strategies and is-a relationships (using `derive`): https://slides.com/jochenbedersdorfer/clojure-for-java-developers/#/4/3 – Jochen Bedersdorfer Jun 07 '20 at 02:26
  • 2
    What happens when `foo` isn't under your control? Say a user wants to add another option to `foo`. Which version will be easier to extend? – Carcigenicate Jun 07 '20 at 03:47
  • I don't see how that makes any difference Carcigenicate. Foo is just a function you can define anywhere whether multimethod or method using instanceof. – Daniel Jun 07 '20 at 04:16
  • 2
    @Daniel The difference is that I can unilaterally decide to define a new foo method to define and maintain functionality for a new class without touching the existing implementation. – mac Jun 07 '20 at 09:17
  • 1
    The multimethod is not really equivalent to the `instanceof` version. It's a bit like saying that a function that returns `x^2` is equivalent to `64` because in your code you only ever call `square(8)`. The `instanceof` version pre-determines all of the possible classes of `t` in advance. An equivalent method would say "get the class of `t` and search all of the loaded code for a method meant to be called when `t` has that class." That class doesn't even need to have existed when you wrote the original code. – jas Jun 07 '20 at 12:20
  • Thanks! I missed that part. So you can define foo to handle a bunch of cases and someone else can extend it later without access to the code to handle more cases. – Daniel Jun 07 '20 at 15:59

1 Answers1

2

One of the benefits discussed in the comments is that the defmulti and defmethod can be done in different files, by different users. An excellent example is Clojure's own print-method multi-method.

From the docs, we see how we can define a custom print-method for a new record type we create:

(deftype XYZ [])

; without custom print-method defined:
user=> (prn (XYZ.))
#<XYZ user.XYZ@2670d85b> 

; Note, this hooks into the pre-existing `(defmulti print-method ...)`
(defmethod print-method XYZ [v ^java.io.Writer w] 
  (.write w "<<-XYZ->>"))

; with print-method
user=> (prn (XYZ.))
<<-XYZ->>

So while it has similarity to a giant cond statement, it is more flexible & cleaner.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48