Have you seen David Nolen's conj 2011 talk, "Predicate Dispatch"? It
covers exactly this material, but looks at trying to be somewhat more
efficient by not making method dispatch O(n) in the number of
implementations. At least as far as I understand it anyway.

http://lanyrd.com/2011/clojure-conj/shhfg/

Phil

On 7 November 2012 18:08, Matt Ridsdale <mridsd...@gmail.com> wrote:
> Hi,
>
> I was thinking about multimethods and the flexibility given by being able to
> dispatch on the value of an arbitrary function of the arguments,
> instead of just the class of the first argument as in Java. It seems to me
> that we could be even more flexible if each method implementation
> was associated with a predicate function of the args, instead of being
> associated with a fixed dispatch value. See the below code for an example of
> what I mean:
>
> (ns polymorphism
>   (:use clojure.test))
>
> (def pm-to-dispatch-pred-map (ref {}))
>
> ; these are kind of like multimethods, so let's give them a similar name -
> polymethods.
> (defmacro defpoly [a-name [& args]]
>   "define a polymethod signature"
>   `(do
>      (defn ~a-name [~@args]
>          (let [dispatch-pred-to-impl-fn-map# (get
> @polymorphism/pm-to-dispatch-pred-map ~a-name)
>                matching-keys# (filter #(apply % [~@args]) (keys
> dispatch-pred-to-impl-fn-map#))
>                num-matches# (count matching-keys#)]
>            (case num-matches#
>              0 (throw (Exception. (str "No matching function defined for
> polymethod " ~a-name " and args " [~@args])))
>              1 (apply (get dispatch-pred-to-impl-fn-map# (first
> matching-keys#)) [~@args])
>              (throw (Exception. (str num-matches# " matching functions
> defined for polymethod " ~a-name " and args " [~@args]))))))
>        (dosync (alter polymorphism/pm-to-dispatch-pred-map assoc ~a-name
> {}))))
>
> (defmacro defpolyimpl [a-name dispatch-pred & body]
>   "define a polymethod implementation, for a given dispatch predicate"
>   `(let [dispatch-pred-to-impl-fn-map# (get
> @polymorphism/pm-to-dispatch-pred-map ~a-name)]
>      (if (nil? dispatch-pred-to-impl-fn-map#)
>        (throw (Exception. "No such polymethod: " ~a-name)))
>      (let [impl-fn# (fn ~@body)]
>        (dosync (alter polymorphism/pm-to-dispatch-pred-map assoc ~a-name
> (assoc dispatch-pred-to-impl-fn-map# ~dispatch-pred impl-fn#)))
>        ~a-name)))
>
>
> (deftest polymorphism-test
>   (defpoly find-name [a-map])
>
>   (defpolyimpl find-name
>     #(and (contains? % :first-name) (contains? % :surname))
>     [a-map]
>     (str (:first-name a-map) " "  (:surname a-map)))
>
>   (defpolyimpl find-name
>     #(contains? % :full-name)
>     [a-map]
>     (:full-name a-map))
>
>   (def personA {:first-name "John" :surname "Smith"})
>   (def personB {:full-name "Jane Bloggs"})
>   (is (= "John Smith" (find-name personA)))
>   (is (= "Jane Bloggs" (find-name personB))))
>
> (run-tests)
>
> I think this system of "dispatch predicates" instead of dispatch values is
> more flexible/expressive, because you can easily emulate multimethods: if
> your multimethod had dispatch function dispatch-fn and a dispatch value
> dispatch-val, you would use
>   #(isa? (apply dispatch-fn %&) dispatch-val)
> as the dispatch predicate. Whereas I can't see a simple way to express an
> arbitrary set of "polymethods" in terms of multimethods.
>
> There are a few problems I can see with this approach, but none of them seem
> to be dealbreakers to me:
> - You need to filter a list to determine the appropriate function
> implementation, whereas multimethods just look up the dispatch value in a
> hash map. This could decrease performance if there are many implementations.
> - In this version, they don't compose well: somebody could define a new
> implementation somewhere else which breaks your code, because two dispatch
> predicates match the args you're using. We could get around this with
> something like prefer-method, or by remembering the order in which
> implementations are defined, and always taking the first/last one which
> matches the args.
> - Maybe this shouldn't be called polymorphism, since there is no notion of
> type involved anywhere, except for the "types" defined by the predicates.
>
> Is there anything wrong with this approach, or any particular reason it
> wasn't done this way in Clojure?
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to