views:

106

answers:

2

Suppose I have a bunch of Clojure data structures, all of the same type - for example an object type defined by defrecord.

What is the best way to get polymorphic behaviour across these structures?

Would it be good practice to embed a function within the structure so that I can do something like:

((:my-method my-object) param1 param2)

Or is there a better way?

+3  A: 

You can use multimethods for dispatch,

(derive ::subtype ::type)

then you can dispatch based on type

(defmulti my-method
  (fn [type] ((meta type) :type)))

(defmethod my-method ::subtype []
  ;;stuff...
 )

You need to add :type to the objects metadata when you create it,

(with-meta [1 2 3] {:type ::subtype})

or I haven't tried this but since protocols and records creates java classes behind the scenes you dispatch based on that name.

Hamza Yerlikaya
Note: `(fn [type] ((meta type) :type))` is just called [`type`](http://clojure.github.com/clojure/branch-1.1.x/clojure.core-api.html#clojure.core/type).
kotarak
A: 

If I understand your question, protocols are what you want to use.

user=> (defprotocol P (foo [p x]))
P
user=> (defrecord R [a b] P (foo [_ x] [a b x]))
user.R
user=> (defrecord S [a b] P (foo [_ x] [x b a]))
user.S
user=> (def r (R. 1 2))
#'user/r
user=> (def s (S. 1 2))
#'user/s
user=> (foo r 3)
[1 2 3]
user=> (foo s 3)
[3 2 1]

In the above both R and S implements the foo function defined by protocol P, but they each do so differently. When the foo function is called, it dispatches on type of the first arg.

Alex Taggart
I'm using protocols/defrecord already - the point is that I want polymorphic behaviour between objects of the same type (i.e. multiple instances of R in your example)
mikera
Then you want probably multimethods. `(defmulti foo (fn [R-instance] (if (= :full (:phase-of-moon R-instance)) :do-this :do-that)) (defmethod foo :do-this ...) ...`
kotarak