Re: Polymorphism based on predicates instead of dispatch value types

145 views
Skip to first unread message

Raoul Duke

unread,
Nov 7, 2012, 1:48:47 PM11/7/12
to clo...@googlegroups.com
On Wed, Nov 7, 2012 at 10:08 AM, Matt Ridsdale <mrid...@gmail.com> wrote:
> 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

ah, so you can be even less likely to ever statically know what cases
you've covered? :-)

Matt Ridsdale

unread,
Nov 8, 2012, 4:40:18 AM11/8/12
to clo...@googlegroups.com
Keep your static concerns out of my dynamic language ; )

Seriously, do you think/know that's the reason people don't do this? Even in Java, you don't know which method implementation will be called until runtime. And you can define a default polyimpl with an always-true predicate, once you've chosen a solution for handling cases with more than one matching implementation.

Philip Potter

unread,
Nov 8, 2012, 5:29:04 AM11/8/12
to clo...@googlegroups.com
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 <mrid...@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 clo...@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+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

Matt Ridsdale

unread,
Nov 9, 2012, 6:51:07 AM11/9/12
to clo...@googlegroups.com
Thanks for the pointer, I haven't seen it and I'll be sure to give it a look when I have some time.

Matt
Reply all
Reply to author
Forward
0 new messages