avoiding duplication using multi-methods

3 views
Skip to first unread message

Parth Malwankar

unread,
Aug 31, 2008, 11:48:50 PM8/31/08
to Clojure
Hello,

I have a situation in which different multi-methods
need similar behavior with different arguments.

So they call the same function (one-or-two below)
with the arguments. E.g.

(defmulti what-num (fn [x] x))

(defn one-or-two [x] (println "saw one or two: " x))

(defmethod what-num :one [x] (one-or-two x))
(defmethod what-num :two [x] (one-or-two x))
(defmethod what-num :three [x] (println "saw 3"))
(defmethod what-num :four [x] (println "saw 4"))

user=> (what-num :one)
saw one or two: :one
nil
user=> (what-num :two)
saw one or two: :two
nil
user=> (what-num :three)
saw 3

This works fine. However, I was wondering if its at all
possible to return duplication of creating the
methods 'what-num :one [x]' and 'what-num :two [x]'.

Can we have something to the effect of
'what-num (or :one :two) [x]'? This would help get
rid of one-or-two and I would be able to define a single
what-num for this.

I could have 'defmulti what-num' conditionally return
something like :one-or-two but I am not sure if thats such
a good idea as I might end up with a big 'cond' over
time.

What would be a good way to do something like this?

Thanks.
Parth

darren...@gmail.com

unread,
Sep 1, 2008, 3:26:43 AM9/1/08
to Clojure
I don't know if it is a "good" way, but here is what I used for a
similar problem:

(defmacro defmethod-for-values
[multifn dispatch-values-seq & fn-tail]
`(let [pvar# (var ~multifn)
method# (fn ~@fn-tail)]
(doseq dispatch-val# ~dispatch-values-seq
(. pvar# (commuteRoot (fn [#^clojure.lang.MultiFn mf#]
(. mf#
(assoc dispatch-val# method#))))))))

Which is just a wrapper around the defmethod macro that allows you to
specify a list of values instead of a single value that will be mapped
to the method.

This would allow you to define your above example with:

(defmulti what-num (fn [x] x))

(defmethod-for-values what-num '(:one :two) [x] (println "saw one or
two: " x))
(defmethod what-num :three [x] (println "saw 3"))
(defmethod what-num :four [x] (println "saw 4"))


See this thread for more details:

http://groups.google.com/group/clojure/browse_thread/thread/eed9015c2456e336/8822ecd0d7f54b8b

Hope this helps,
--Darren

Parth Malwankar

unread,
Sep 1, 2008, 4:31:45 AM9/1/08
to Clojure
Thanks Darren.
Along the lines of your macro I created the one
below that avoids clojure.lang.*.
I keep forgetting this is lisp and we can write a macro :P

Here is what I could come up with.

(defmulti what-num (fn [x] x))

(defmacro defmethod-in [n dval-coll args & body]
(let [get-method (fn [d] `(defmethod ~n ~d ~args ~@body))]
`(do ~@(map get-method dval-coll))))

(defmethod-in what-num [:one :two] [x] (println "1 or 2" x))
(defmethod what-num :three [x] (println "saw 3"))
(defmethod what-num :four [x] (println "saw 4"))
(defmethod what-num :default [x] :default)

In case this can be improved further, comments welcome.
Seems to be working fine so far:

user=> (what-num :four)
saw 4
nil
user=> (what-num :one)
1 or 2 :one
nil
user=> (what-num :two)
1 or 2 :two
nil
user=> (what-num :three)
saw 3
nil
user=> (what-num :five)
:default

Parth



> Which is just a wrapper around the defmethod macro that allows you to
> specify a list of values instead of a single value that will be mapped
> to the method.
>
> This would allow you to define your above example with:
>
> (defmulti what-num (fn [x] x))
>
> (defmethod-for-values what-num '(:one :two) [x] (println "saw one or
> two: " x))
> (defmethod what-num :three [x] (println "saw 3"))
> (defmethod what-num :four [x] (println "saw 4"))
>
> See this thread for more details:
>
> http://groups.google.com/group/clojure/browse_thread/thread/eed9015c2...
>
> Hope this helps,
> --Darren
Reply all
Reply to author
Forward
0 new messages