Multiple dispatch with Multimethods

6 views
Skip to first unread message

Tzach

unread,
Dec 7, 2009, 4:21:48 AM12/7/09
to Clojure
Hello
What is the idiomatic way to implement the classic collide-with
function in Clojure?
I would like to implement something similar to the following (pseudo
code ahead):

(defmulti collide-with foo)

(defmethod collide-with ["asteroid" "spaceship"] (print "Boom"))
(defmethod collide-with ["asteroid" any] (print " Wiiissh"))
(defmethod collide-with [any "spaceship"] (print "Wooossh"))
(defmethod collide-with [any any] (print "Dead Space"))

The idea is to have a default function for a collision of asteroid
with anything else, and of spaceship with anything else.
What foo can I use?

Thanks

ataggart

unread,
Dec 7, 2009, 4:59:06 AM12/7/09
to Clojure
If order doesn't matter:

(defmulti collide-with
#(vec (filter #{"asteroid" "spaceship"} %&)))
(defmethod collide-with ["asteroid" "spaceship"] [x y] (print
"Boom"))
(defmethod collide-with ["asteroid"] [x y] (print " Wiiissh"))
(defmethod collide-with ["spaceship"] [x y] (print "Wooossh"))
(defmethod collide-with [] [x y] (print "Dead Space"))

Meikel Brandmeyer

unread,
Dec 7, 2009, 5:10:47 AM12/7/09
to Clojure
Hi,

On Dec 7, 10:21 am, Tzach <tzach.livya...@gmail.com> wrote:

> (defmulti collide-with foo)
>
> (defmethod collide-with ["asteroid" "spaceship"] (print "Boom"))
> (defmethod collide-with ["asteroid" any] (print " Wiiissh"))
> (defmethod collide-with [any "spaceship"] (print "Wooossh"))
> (defmethod collide-with [any any] (print "Dead Space"))
>
> The idea is to have a default function for a collision of asteroid
> with anything else, and of spaceship with anything else.
> What foo can I use?

Eg. a custom hierarchy. Since there is no bottom type, you have to
create it manually. Deriving Object from ::Any takes care of the Java
land. For clojure land you have to derive each type from ::Any (or
anything already deriving from ::Any).

(def space-hierarchy
(-> (make-hierarchy)
(derive Object ::Any)
(derive ::Spaceship ::Any)
(derive ::Asteroid ::Any)))

(defmulti collide-with
#(vec (map type %&))
:hierarchy #'space-hierarchy)

(defmethod collide-with :default
[_ _]
(print "Dead Space"))

(defmethod collide-with [::Asteroid ::Any]
[_ _]
(print "Wiiissh"))

(defmethod collide-with [::Any ::Spaceship]
[_ _]
(print "Wooosssh"))

(defmethod collide-with [::Asteroid ::Spaceship]
[_ _]
(println "Booom!"))

Maybe you have to disambiguate with prefer-method in certain cases. (I
don't think, that's the case here, but I haven't tested it...)

Hope this helps.

Sincerely
Meikel

Tzach

unread,
Dec 8, 2009, 2:36:40 AM12/8/09
to Clojure
Thanks
I didn't know the costume hierarchy at all, it seems to be very
useful.
I understand it is coupled to the Java object hierarchy.
Is there a way to create similar util for strings, as I did on my
first example?

Meikel Brandmeyer

unread,
Dec 8, 2009, 3:34:48 AM12/8/09
to Clojure
Hi,

On Dec 8, 8:36 am, Tzach <tzach.livya...@gmail.com> wrote:

> I didn't know the costume hierarchy at all, it seems to be very
> useful.
> I understand it is coupled to the Java object hierarchy.

Not at all. Have a look at the docstrings, eg. of derive and isa?.
Even in my example ::Asteroid and ::Spaceship are not Java classes but
simply qualified keywords. To use: (with-meta {:engines 4 :lasers
2 :shields :ion} {:type ::Spaceship})...

> Is there a way to create similar util for strings, as I did on my
> first example?

For strings this is not possible, since you can't use it in derive. So
there is no bottom type possible. You would have to do:

(defmethod collide-with ["asteroid" "spaceship"] ...)
(defmethod collide-with ["asteroid" "comet"] ...)
(defmethod collide-with ["asteroid" "asteroid"] ...)
(defmethod collide-with ["asteroid" "jupiter"] ...)
....

Sincerely
Meikel
Reply all
Reply to author
Forward
0 new messages