defmethod catch all
Asked Answered
F

3

8

I have a multimethod that specializes on two parameters:

(defmulti get-tag-type (fn [type tag] [type tag]))

Having the type allows me to group the different defmethod calls into sets:

(defmethod get-tag-type [::cat 0] [type tag] ::tiger)
(defmethod get-tag-type [::cat 1] [type tag] ::lion)
(defmethod get-tag-type [::cat 2] [type tag] ::jaguar)

(defmethod get-tag-type [::dog 0] [type tag] ::poodle)
(defmethod get-tag-type [::dog 1] [type tag] ::australian-shepherd)
(defmethod get-tag-type [::dog 2] [type tag] ::labrador-retriever)

However, sometimes, I want a catch all or default for one of the groups, which would be called if none of the others matched:

(defmethod get-tag-type [::dog :default] ::mutt)

However, this doesn't work unless tag is actually :default.

What is a good way to accomplish this?

Fustigate answered 25/2, 2012 at 1:20 Comment(0)
F
7

Your dispatch function needs to know which mappings are already defined so that it can decide when to resort to a default. The methods function will return those mappings to you.

(defmulti get-tag-type (fn [type tag] 
                         (let [mlist (methods get-tag-type)]
                           (if-let [d (get mlist [type tag])] 
                             [type tag]
                             [type :default]))))
Fading answered 25/2, 2012 at 6:0 Comment(0)
T
8

Multimethods support a fallback method, identified by using the (configurable) value :default , if none of the other methods match.

In your case, you'd simply add:

(defmethod get-tag-type :default [type tag]
  (do special behaviour here ...))

Given your example, it might be worth noting that you can establish hierarchies using Clojure keywords, and multimethod dispatch understands those hierarchies: http://clojure.org/multimethods

Tinsmith answered 25/2, 2012 at 8:17 Comment(1)
Unfortunately, I need the default behavior to be different for different values of type, and I believe only one :default can be defined per multi method.Fustigate
F
7

Your dispatch function needs to know which mappings are already defined so that it can decide when to resort to a default. The methods function will return those mappings to you.

(defmulti get-tag-type (fn [type tag] 
                         (let [mlist (methods get-tag-type)]
                           (if-let [d (get mlist [type tag])] 
                             [type tag]
                             [type :default]))))
Fading answered 25/2, 2012 at 6:0 Comment(0)
I
0

You could alter the defmulti to:

(defmulti get-tag-type (fn [type tag] 
                         (if (= type ::dog)
                             ::dog
                             [type tag])))

Then write your method like this:

(defmethod get-tag-type ::dog ::mutt)
Inessa answered 25/2, 2012 at 1:36 Comment(1)
I don't want the defmulti to know about the defmethods that will be using it.Fustigate

© 2022 - 2024 — McMap. All rights reserved.