per-defmulti hierarchies

67 views
Skip to first unread message

Meikel Brandmeyer

unread,
Jan 5, 2009, 4:56:47 PM1/5/09
to clo...@googlegroups.com
Dear Clojurians,

This patch allows multimethods to use different hierarchies, than
the global hierarchy for use with isa?. Currently only the global
hierarchy is possible.

The patch extends the MultiFn class to accept also a Var pointing
to a hierarchy. The rationale is, that otherwise the multimethod
cannot be extended with derivation after it's definition. Hence
it cannot receive the hierarchy directly.

To promote that every hierarchy must be a Var, derive and underive
are modified to act on vars instead of the hierarchies directly.
Furthermore the convenience macros defhierarchy and defhierarchy-
are provided, which define new hierarchies with optional docstring
support and automatic initialisation with make-hierarchy.

This is listed as a "hot" item in the Todo list and is connected
to http://code.google.com/p/clojure/issues/detail?id=8.
In fact the patch there is needed as a prerequisite.

Example of usage:

(defhierarchy our-hierarchy
"This is a dedicated hierarchy for our own purposes!")

; Note: here we need the Var, hence the #'.
; Should this be handled via a macro?
(derive #'our-hierarchy :xxx :yyy)

(defmulti foo identity :hierarchy our-hierarchy)

(defmethod foo :yyy
[x]
(println "We are in" :yyy "but got" x))

Comments and feedback appreciated.

Sincerely
Meikel

issue-8a.diff

Rich Hickey

unread,
Jan 7, 2009, 11:10:24 AM1/7/09
to Clojure


On Jan 5, 4:56 pm, Meikel Brandmeyer <m...@kotka.de> wrote:
> Dear Clojurians,
>
> This patch allows multimethods to use different hierarchies, than
> the global hierarchy for use with isa?. Currently only the global
> hierarchy is possible.
>
> The patch extends the MultiFn class to accept also a Var pointing
> to a hierarchy. The rationale is, that otherwise the multimethod
> cannot be extended with derivation after it's definition. Hence
> it cannot receive the hierarchy directly.
>
> To promote that every hierarchy must be a Var, derive and underive
> are modified to act on vars instead of the hierarchies directly.
> Furthermore the convenience macros defhierarchy and defhierarchy-
> are provided, which define new hierarchies with optional docstring
> support and automatic initialisation with make-hierarchy.
>
> This is listed as a "hot" item in the Todo list and is connected
> tohttp://code.google.com/p/clojure/issues/detail?id=8.
> In fact the patch there is needed as a prerequisite.
>
> Example of usage:
>
> (defhierarchy our-hierarchy
> "This is a dedicated hierarchy for our own purposes!")
>
> ; Note: here we need the Var, hence the #'.
> ; Should this be handled via a macro?
> (derive #'our-hierarchy :xxx :yyy)
>
> (defmulti foo identity :hierarchy our-hierarchy)
>
> (defmethod foo :yyy
> [x]
> (println "We are in" :yyy "but got" x))
>
> Comments and feedback appreciated.
>

Thanks Meikel. I have a couple of points:

First, making derive and underive act on vars instead of the
hierarchies directly is not good - it reduces the generality
needlessly. Good practice is to make pure fns, then do the reference
part, not mix the two.

Second, there's no reason to bind MultiFn to Var - any IRef will do.
Then people can put their hierarchies in Atoms/Refs or whatever.

Third, I don't see the need for defhierarchy at this point.

Rich

Meikel Brandmeyer

unread,
Jan 7, 2009, 11:44:34 AM1/7/09
to clo...@googlegroups.com
Hi Rich,

Am 07.01.2009 um 17:10 schrieb Rich Hickey:

> Thanks Meikel. I have a couple of points:
>
> First, making derive and underive act on vars instead of the
> hierarchies directly is not good - it reduces the generality
> needlessly. Good practice is to make pure fns, then do the reference
> part, not mix the two.
>
> Second, there's no reason to bind MultiFn to Var - any IRef will do.
> Then people can put their hierarchies in Atoms/Refs or whatever.
>
> Third, I don't see the need for defhierarchy at this point.

Ok. My problem is, that passing the hierarchy directly
doesn't allow adding further derivations. The only idea
I had, was to use Vars. I didn't like the idea, but I
couldn't come up with another one. The solution is again
an abstraction: IRef. Changing (un)derive and adding
defhierarchy, was just some try to help the user with
the limitations of this approach.

But (dosync (alter ref-to-hierarchy derive ::Foo ::Bar))
works perfectly. :) Much cleaner indeed. This shows
again: ugly solutions and special cases are a sign,
that one does it wrong...

Please find attached another patch going for IRef instead
of Var directly. And without the other cruft.

Sincerely
Meikel

issue-8b.diff

Meikel Brandmeyer

unread,
Jan 11, 2009, 4:43:22 AM1/11/09
to clo...@googlegroups.com
Hello,

Am 07.01.2009 um 17:44 schrieb Meikel Brandmeyer:

> Please find attached another patch going for IRef instead
> of Var directly. And without the other cruft.

Another update in sync with updated issue #8.

Sincerely
Meikel

issue-8b.diff

Mark Fredrickson

unread,
Jan 20, 2009, 12:13:14 AM1/20/09
to clo...@googlegroups.com
On the subject of per defmulti hierarchies:

The recent CLJOS thread got me thinking about this problem again.
While allowing per-multimethod hierarchies solves a large number
problems, I think it might be lacking for a class of needs. I think I
have solution that can provide for multiple hierarchies and meet this
larger class to which I allude. Allow me to provide an illustration.
Say I have a step-wise mathematical function. From less than or equal
to zero, return zero. From more than zero to less than equal to five,
return the value. Above 5, square the value.

I could implement that as multimethod:

(defmulti stepwise (fn [n] (cond (>= 0 n) :zero (and (> n 0) (< n
5)) :zero-five :else :above-five)))
(defmethod stepwise :zero [_] 0)
(defmethod stepwise :zero-five [x] x)
(defmethod stepwise :above-five [x] (* x x))

I've left an intentional programming error in to show how easy it is
to make an error with such (cond ...) based dispatch. Yuck.

But there is a better way. What if I could supply my own function to
decide which key matched the multimethod (not what the key is for the
value, but how the key maps to a multimethod). Currently all we have
is isa?, but if this were only the default? My example might look
something like:

(defmulti stepwise identity <=)
(defmethod stepwise 0 [_] 0)
(defmethod stepwise 5 [n] n)
(defmethod stepwise :default [n] (* n n))
(prefer-method stepwise 0 5) ; since -1 < 0 and -1 < 5

I find this much cleaner, and the subtle error above was been
eliminated. And if I want to add another case, its not too hard
(though the number of prefer-methods might increase exponentially).
This is a rather trivial case, but I think it makes the need clear:
per-multi comparison functions.

For multiple hierarchies, this would be a common idiom:
(defmulti mymulti dispatch-function (fn [object-key method-key] (isa?
*my-hiearchy* object-key method-key))

A user could use a ref or atom as needed, no special casing in the
Clojure code.

What do people think?
-Mark

> <issue-8b.diff>

- Mark Fredrickson
mark.m.fr...@gmail.com
http://www.markmfredrickson.com


Rich Hickey

unread,
Jan 20, 2009, 9:10:32 AM1/20/09
to Clojure


On Jan 20, 12:13 am, Mark Fredrickson <mark.m.fredrick...@gmail.com>
wrote:
This seems to me to be a half step towards predicate dispatch. With
the a la carte hierarchies freeing us from type-based inheritance, it
might be worth someone revisiting Chambers/Chen style predicate
dispatch.

Rich
Reply all
Reply to author
Forward
0 new messages