Enhanced Multimethods

103 views
Skip to first unread message

Rich Hickey

unread,
Jul 27, 2008, 10:12:27 PM7/27/08
to Clojure
I've added a significant enhancement to the multimethod system. One
limitation of the multimethod system has been an inability to leverage
inheritance in dispatching, i.e. all dispatching was done via =
semantics, so you couldn't dispatch on an interface type, for
instance.

Not being that fond of the limitations of types, both the static
nature of Java types and the normal OO assignment of a single type to
an object, I wanted to be able to leverage types, but also allow for
independent, dynamic, hierarchical taxonomies to be defined, and used
anywhere - as attributes, metadata, etc.

The extension is based on a la carte symbolic hierarchies and isa?
based dispatch.

------------- A la carte symbolic hierarchies ------------------

You can now define hierarchical relationships with (derive child
parent). Child and parent can be either symbols or keywords, and must
be namespace-qualified:

Note, new reader syntax, ::keywords resolve namespaces

user=> ::rect
:user/rect

;derive is the fundamental relationship-maker
(derive ::rect ::shape)
(derive ::square ::rect)

;parents/ancestors/descendants/isa? let you query the hierarchy
(parents ::rect)
#{:user/shape}

(ancestors ::square)
#{:user/rect :user/shape}

(descendants ::shape)
#{:user/rect :user/square}

;(= x y) implies (isa? x y)
(isa? 42 42)
true

;isa? uses the hierarchy system
(isa? ::square ::shape)
true

You can also use a class as the child (but not the parent, the only
way to make something the child of a class is via Java inheritance).
This allows you to superimpose new taxonomies on the existing Java
class hierarchy:

(derive java.util.Map ::collection)
(derive java.util.Collection ::collection)

(isa? java.util.HashMap ::collection)
true

;isa? also tests for class relationships:
(isa? String Object)
true

;isa? works with vectors by calling isa? on their corresponding
elements:
(isa? [::square ::rect] [::shape ::shape])
true

;as do parents/ancestors (but not descendants, since class descendants
are an open set)
(ancestors java.util.ArrayList)
#{java.lang.Cloneable java.lang.Object java.util.List
java.util.Collection java.io.Serializable
java.util.AbstractCollection
java.util.RandomAccess java.util.AbstractList}

------------- isa? based dispatch -------------

Multimethods now use isa? rather than = when testing for dispatch
value matches. Note above that the first test of isa? is =, so exact
matches work as before.

(defmulti foo class)
(defmethod foo ::collection [c] :a-collection)
(defmethod foo String [s] :a-string)

(foo [])
:a-collection

(foo (java.util.HashMap.))
:a-collection

(foo "bar")
:a-string

All of the examples above use the global hierarchy used by the
multimethod system, but entire independent hierarchies can also be
created with make-hierarchy, and all of the above functions take an
optional hierarchy as a first argument.

I hope you find this a powerful and flexible enhancement, which
retains arbitrary dispatch functions and the resulting polymorphism
independent of types.

Feedback welcome,

Rich

Stephen C. Gilardi

unread,
Jul 28, 2008, 9:21:34 AM7/28/08
to clo...@googlegroups.com

On Jul 27, 2008, at 10:12 PM, Rich Hickey wrote:

> ;(= x y) implies (isa? x y)
> (isa? 42 42)
> true
>

> ;isa? also tests for class relationships:
> (isa? String Object)
> true

Is it intentional that isa? doesn't also check instance to class
relationships? I know we have instance? for that and we can manually
take the class of an object, but is there a downside to this:

(isa? 3 Integer)
true

--Steve

Graham Fawcett

unread,
Jul 28, 2008, 9:32:08 AM7/28/08
to clo...@googlegroups.com
On Sun, Jul 27, 2008 at 10:12 PM, Rich Hickey <richh...@gmail.com> wrote:
>
> I've added a significant enhancement to the multimethod system. One
> limitation of the multimethod system has been an inability to leverage
> inheritance in dispatching, i.e. all dispatching was done via =
> semantics, so you couldn't dispatch on an interface type, for
> instance.

Interesting! This is a much-needed improvement -- the old multimethod
dispatch was Clojure's biggest wart, IMO. I look forward to testing
this.

Graham

Rich Hickey

unread,
Jul 28, 2008, 10:08:02 AM7/28/08
to Clojure


On Jul 28, 9:21 am, "Stephen C. Gilardi" <scgila...@me.com> wrote:
> On Jul 27, 2008, at 10:12 PM, Rich Hickey wrote:
>
> > ;(= x y) implies (isa? x y)
> > (isa? 42 42)
> > true
>
> > ;isa? also tests for class relationships:
> > (isa? String Object)
> > true
>
> Is it intentional that isa? doesn't also check instance to class
> relationships?

Yes, very much so. These are 2 different operations that should never
be merged. One checks for membership in a category and the other the
relationship between categories. It may be that isa? isn't the best
name if it causes this confusion. Most properly it might be derives?
or something, but most names have a certain awkwardness in the prefix
form.

> I know we have instance? for that and we can manually
> take the class of an object, but is there a downside to this:
>
> (isa? 3 Integer)
> true
>

Many, here's a simple example:

(isa? String Class)

From a category perspective the answer is no (Strings are not
Classes), from an instance perspective the answer is yes (String is an
instance of Class). Java has isAssignableFrom and instanceof for these
two purposes, and Clojure (now) has isa? and instance?.

At this early stage, I'm open to alternative names for isa?

Rich

Stuart Sierra

unread,
Jul 28, 2008, 10:30:10 AM7/28/08
to Clojure
On Jul 27, 10:12 pm, Rich Hickey <richhic...@gmail.com> wrote:
> I've added a significant enhancement to the multimethod system. One
...
> The extension is based on a la carte symbolic hierarchies and isa?
> based dispatch.

Nice! This makes multimethods much more useful.

> At this early stage, I'm open to alternative names for isa?

Maybe "derived?" Or "deriv?" for short.

-Stuart

Randall R Schulz

unread,
Jul 28, 2008, 10:34:50 AM7/28/08
to clo...@googlegroups.com
On Monday 28 July 2008 07:08, Rich Hickey wrote:
> ...

>
> Many, here's a simple example:
>
> (isa? String Class)
>
> From a category perspective the answer is no (Strings are not
> Classes), from an instance perspective the answer is yes (String is
> an instance of Class). Java has isAssignableFrom and instanceof for
> these two purposes, and Clojure (now) has isa? and instance?.

This is a recurring issue, mostly just because of the terminology used
is confusing and inaccurate.

I'd recommend isa? be the relation between individuals / instances and
class / categories, true when the individual / instance is a member of
the class / category. Then use ako? (A Kind Of) for the relation
between classes / categories, meaning that a sub-type / sub-class
relation.

Examples:

- (isa? "some string" String) ==> true
- (isa? "other string" Object) ==> true
[everything except nil isa? Object]
- (isa? "string string" Number) ==> false

- (ako? String Object) ==> true
[again, everything except nil isa? Object]
- (ako? Integer Number) ==> true
- (ako? Number Integer) ==> false

- (isa? String Class) ==> true
- (isa? "string" Class) ==> false
- (ako? "string" String) ==> false
- (ako? String String) ==> true
[ako? is reflexive and antisymmetric]


> At this early stage, I'm open to alternative names for isa?
>
> Rich


Randall Schulz

Stephen C. Gilardi

unread,
Jul 28, 2008, 10:53:35 AM7/28/08
to clo...@googlegroups.com

On Jul 28, 2008, at 10:08 AM, Rich Hickey wrote:

Many, here's a simple example:

(isa? String Class)

From a category perspective the answer is no (Strings are not
Classes), from an instance perspective the answer is yes (String is an
instance of Class). Java has isAssignableFrom and instanceof for these
two purposes, and Clojure (now) has isa? and instance?.

At this early stage, I'm open to alternative names for isa?

Thanks for the explanation and example.

It seems one phrase  that matches what "isa?" does is "is a kind of". That seems a little to long to work well. Maybe "kind-of"?   (kind-of? 3 Integer) -> false,  (kind-of? Integer Number) -> true, (kind-of? 3 3) -> true.

You described this "kind of" relationship as "category perspective".  Perhaps "subcategory?" or "sub-cat?"

Some thesaurus work led to "species?" which may be short enough to be palatable and odd looking enough that it will gain a distinct jargon meaning for Clojure programmers.

derives-from? seems very clear.  (derives-from? 3 3) doesn't read very well though.


Of all these my current favorite is "kind-of?" .

--Steve

Rich Hickey

unread,
Jul 28, 2008, 11:17:43 AM7/28/08
to Clojure
The terminology isn't confusing/inaccurate as much as overloaded. If
you ask people if a car is a vehicle they are not confused. Nor are
they if you ask them if this/that car is a vehicle, i.e. isa is used
for both cases.

In any case, instance? is a done deal for the instance of a class
relationship. It partially binds nicely, is unambiguous, and is
already in use.

All that is open is the name for the sub-class relation predicate. I
don't like ako?, being an acronym and not in widespread use. I guess
kindof? kind? derives? etc are possibilities. One constraint I have is
I'd like the argument order to match derive.

Rich

Rich Hickey

unread,
Jul 28, 2008, 12:02:25 PM7/28/08
to Clojure
I just finished and put up the system for disambiguating in case of
multiple matches where neither dominates the other. Basically you can
just declare, per multimethod, that one dispatch value is preferred
over another:

(derive ::rect ::shape)

(defmulti bar (fn [x y] [x y]))
(defmethod bar [::rect ::shape] [x y] :rect-shape)
(defmethod bar [::shape ::rect] [x y] :shape-rect)

(bar ::rect ::rect)
-> java.lang.IllegalArgumentException: Multiple methods match dispatch
value

(prefer-method bar [::rect ::shape] [::shape ::rect])

(bar ::rect ::rect)
-> :rect-shape

Rich

On Jul 27, 10:12 pm, Rich Hickey <richhic...@gmail.com> wrote:

Graham Fawcett

unread,
Jul 28, 2008, 12:18:00 PM7/28/08
to clo...@googlegroups.com
On Mon, Jul 28, 2008 at 12:02 PM, Rich Hickey <richh...@gmail.com> wrote:
>
> I just finished and put up the system for disambiguating in case of
> multiple matches where neither dominates the other. Basically you can
> just declare, per multimethod, that one dispatch value is preferred
> over another:
>
> (derive ::rect ::shape)
>
> (defmulti bar (fn [x y] [x y]))
> (defmethod bar [::rect ::shape] [x y] :rect-shape)
> (defmethod bar [::shape ::rect] [x y] :shape-rect)
>
> (bar ::rect ::rect)
> -> java.lang.IllegalArgumentException: Multiple methods match dispatch
> value
>
> (prefer-method bar [::rect ::shape] [::shape ::rect])
>
> (bar ::rect ::rect)
> -> :rect-shape

Reading this, I wonder whether #'vector ought not be the default
dispatch function. It would encourage the use of vectors as dispatch
values, which might make it easier to add specialized methods with
varying arities. That would sort of put a bit more "multi" into
"multimethods" by default.

Just a thought,
Graham

Rich Hickey

unread,
Jul 28, 2008, 12:53:08 PM7/28/08
to Clojure


On Jul 28, 12:18 pm, "Graham Fawcett" <graham.fawc...@gmail.com>
wrote:
I don't think so, for a couple of reasons:

- Single-argument multimethods, or multimethods that dispatch on only
a single argument, will be common.

- When dispatching on multiple arguments, the example notwithstanding,
the dispatch value will still often be a vector of a function of the
arguments than the arguments themselves, e.g. (fn [x y] [(class x)
(class y)])

There should not be any feeling that multi means, or is only for the
case of, multiple arguments - multimethods are the polymorphism
construct of Clojure. I thought about calling them polymethods at one
point.

Rich

Graham Fawcett

unread,
Jul 28, 2008, 1:03:18 PM7/28/08
to clo...@googlegroups.com

Good points, Rich. Suggestion withdrawn.

Graham

Frantisek Sodomka

unread,
Jul 28, 2008, 4:41:59 PM7/28/08
to clo...@googlegroups.com
instanceof (Java) -> instance? (Clojure)
"is a kind of" could therefore be "kind?" in Clojure

Also, I like "derived?" as suggested by Stuart. It shows symmetry "derive"
-> "derived?".

Frantisek


On Mon, 28 Jul 2008 16:08:02 +0200, Rich Hickey <richh...@gmail.com>
wrote:

lpetit

unread,
Jul 28, 2008, 7:45:34 PM7/28/08
to Clojure
Hello,

"kind" is a term already in wide use in the Haskell community (at
least), and it may be confusing to haskell users. They could make bad
"guesses" based on their current knowledge.
Maybe this should be avoided, maybe this doesn't matter. I'm not sure.

What about sticking with "derived?". The symmetry with "derive" is
appealing to me too.

My 0,02€,

--
Laurent

On 28 juil, 22:41, "Frantisek Sodomka" <fa...@intricatevisions.com>
wrote:
> instanceof (Java) -> instance? (Clojure)
> "is a kind of" could therefore be "kind?" in Clojure
>
> Also, I like "derived?" as suggested by Stuart. It shows symmetry "derive"  
> -> "derived?".
>
> Frantisek
>
> On Mon, 28 Jul 2008 16:08:02 +0200, Rich Hickey <richhic...@gmail.com>  

Rich Hickey

unread,
Jul 28, 2008, 9:01:41 PM7/28/08
to Clojure
One problem with derived? is that this relation must also hold for
equality:

(derived? 42 42)

just doesn't seem right.

I guess I still prefer isa?, but there seems to be a presumption in
the responses here that isa? is undesirable. isa? proponents if any,
should speak up,.

So the criteria is:

Must take args as per derived.
Must make sense as prefix.
Must embrace the equality relation as well.

What about is? - seems to imply the instanceof relationship less than
isa?

(is? 42 42)
(is? String Object)
(is? ::rect ::shape)

Rich

Graham Fawcett

unread,
Jul 28, 2008, 9:28:26 PM7/28/08
to clo...@googlegroups.com
On Mon, Jul 28, 2008 at 9:01 PM, Rich Hickey <richh...@gmail.com> wrote:
>
> One problem with derived? is that this relation must also hold for
> equality:
>
> (derived? 42 42)
>
> just doesn't seem right.
>
> I guess I still prefer isa?, but there seems to be a presumption in
> the responses here that isa? is undesirable. isa? proponents if any,
> should speak up,.
>
> So the criteria is:
>
> Must take args as per derived.
> Must make sense as prefix.
> Must embrace the equality relation as well.

Do you forsee this function being used much outside of the multimethod
machinery, or other meta-level code? If not, then perhaps
(derived-or-equal? ...)? It doesn't have to be terribly short if it's
infrequently used.

(is? ...) seems wrong to me -- it suggests an identity comparison.

Best,
Graham

Rich Hickey

unread,
Jul 28, 2008, 9:43:19 PM7/28/08
to Clojure


On Jul 28, 9:28 pm, "Graham Fawcett" <graham.fawc...@gmail.com> wrote:
> On Mon, Jul 28, 2008 at 9:01 PM, Rich Hickey <richhic...@gmail.com> wrote:
>
> > One problem with derived? is that this relation must also hold for
> > equality:
>
> > (derived? 42 42)
>
> > just doesn't seem right.
>
> > I guess I still prefer isa?, but there seems to be a presumption in
> > the responses here that isa? is undesirable. isa? proponents if any,
> > should speak up,.
>
> > So the criteria is:
>
> > Must take args as per derived.
> > Must make sense as prefix.
> > Must embrace the equality relation as well.
>
> Do you forsee this function being used much outside of the multimethod
> machinery, or other meta-level code? If not, then perhaps
> (derived-or-equal? ...)? It doesn't have to be terribly short if it's
> infrequently used.
>

It could be used. I'd like to think this is a generally useful
mechanism for applications that need to reason about relationships.
parents/ancestors/descendants are all straightforward and useful, isa?
needs to be even more so.

> (is? ...) seems wrong to me -- it suggests an identity comparison.
>

Fair enough, still looking.

Rich

lpetit

unread,
Jul 28, 2008, 11:13:51 PM7/28/08
to Clojure

On 29 juil, 03:01, Rich Hickey <richhic...@gmail.com> wrote:
> One problem with derived? is that this relation must also hold for
> equality:
>
> (derived? 42 42)
>
> just doesn't seem right.
>

If the semantics of (derived?) or (isa?) is to compare concepts in a
hierarchy, then (derived?) seems a reasonable choice for the name to
me.
And if one wants to use numbers to represent concepts in one's
hierarchy, then in the context of calling (derived?) or (isa?), it
will not seem weird to use numbers.

If the number 42 does not map to a concept in a hierarchy, it sounds
equally weird to me to use (derived?) or (isa?) for the comparison.

Or, maybe, the contract of (isa?/derived?) may just be to compare
members of hierarchies created by (derive), and not be declared to be
able to be used to test equality (=). And either the equality test
remains for performance or openness of the method (not willing to
throw an exception, either if the arguments to the method have never
been declared as hierarchy members).
And this (=) test may be placed in the multimethod test before calling
(isa?/derived?) ?

Do I miss something ?

--
Laurent

Shawn Hoover

unread,
Jul 29, 2008, 2:28:42 AM7/29/08
to clo...@googlegroups.com
On Mon, Jul 28, 2008 at 6:01 PM, Rich Hickey <richh...@gmail.com> wrote:

One problem with derived? is that this relation must also hold for
equality:

(derived? 42 42)

just doesn't seem right.

I guess I still prefer isa?, but there seems to be a presumption in
the responses here that isa? is undesirable. isa? proponents if any,
should speak up,.

So the criteria is:

Must take args as per derived.
Must make sense as prefix.
Must embrace the equality relation as well.

What about is? - seems to imply the instanceof relationship less than
isa?

(is? 42 42)
(is? String Object)
(is? ::rect ::shape)

I will speak up for isa?.

First, as you say, is? doesn't imply instanceof, but for me it is too close to implying equality. Common Lisp's many equality-like operators are a turnoff, and it would be great for Clojure to avoid that.

Now, describing a hierarchy, taxonomy, or OOP inheritance relationship is most widely understood in programming and life by stating is-a relationships (as indicated in the Wikipedia articles on those topics and anecdotally from life). In life is-a abbreviates "is a kind of" or "is a type of". In OOP is-a offers a succinct, technology-agnostic way of saying "is a subtype of", "specializes", or "inherits". But inherits?, subtype?, and extends? are not good fits for ala carte hierarchies. derived? and specializes? might both be ok, but I believe isa? is the most general applicable term.

It's true is-a can be overloaded to include instanceof relationships, but one could argue that even that meaning only applies to the lowest level of a tree that has it's more interesting structure above those leaves. In any case, the common usage of is-a to describe kinds, types, and specialization makes it very compelling for this situation. It's a nice name, and I doubt you'll find a more applicable use.

If it weren't for the equality dimension, ancestor? would work well, but isa? also wins there. I guess it's an overloaded term for a good reason: it works!

I'll submit two others for their novelty effect:
kinda? -- a play on kind-of? and is-a?
organic? -- as in "a sense of the small imitating the large" from http://en.wikipedia.org/wiki/Hierarchy

Shawn

Christopher Taylor

unread,
Jul 29, 2008, 3:49:30 AM7/29/08
to clo...@googlegroups.com
I agree completely with Shawn's arguments below. In addition, coming
from a Perl background, I quite like isa?, since that's what Perl
uses for inheritance. kinda? is kinda neat, too :).

--Chris

Stephen C. Gilardi

unread,
Jul 29, 2008, 8:29:41 AM7/29/08
to clo...@googlegroups.com

On Jul 28, 2008, at 9:01 PM, Rich Hickey wrote:

> So the criteria is:
>
> Must take args as per derived.
> Must make sense as prefix.
> Must embrace the equality relation as well.

Based on the criteria and all the discussion I'm now in favor of kind? .

I read about Haskell kinds and I don't think there's much chance of
confusion--plus anybody who programs in Haskell has self-selected into
a group of some of the least confusable people on the planet. :-)

Regarding isa?, I think that (isa? 3 Number) being false will always
be an initial surprise to someone learning about this feature of
Clojure. At the very least an example like this should appear in the
documentation explicitly and be explained. One wouldn't have to remain
confused for long, but it doesn't fit well as English. kind? fits
better:

(kind? 3 Number) -> false (3 is not a kind of number)
(kind? Integer Number) -> true (Integer is a kind of number)
(kind? 3 3) -> true (3 is a kind of 3)

And the other examples:

(kind? String Object) -> true
(kind? String Class) -> false ; String is a Class, but it's not a
kind of Class.

I think kind? captures best the fact that the first argument must
(usually) be more than a single instance of the second argument. I
think it also works well with the equality relation: when the second
argument represents a category that includes only 1 thing, it's
natural for that one thing to be a valid representation of that kind,
type, species.

I just noticed (again) that instance? has the opposite order of
arguments. In fact, most times when I write an instance? expression, I
get the order wrong and have to fix it after. I think it's because I'm
considering (op A B) as the Clojure way of writing (A op B) so I'm
expecting "A instance (of) ? B" to be the ordering. The current
ordering is more like ((op A) B) which has a logic to it as well, just
persistently not what I expect.

I looked up Java's instanceof and found its argument ordering is the
opposite of instance?. Rich, I know you make these choices
deliberately. What's the rationale for (instance? c x) rather than
(instance? x c)? Ease of partial application?

--Steve

Rich Hickey

unread,
Jul 29, 2008, 9:17:11 AM7/29/08
to Clojure


On Jul 29, 8:29 am, "Stephen C. Gilardi" <scgila...@me.com> wrote:
> On Jul 28, 2008, at 9:01 PM, Rich Hickey wrote:
>
> > So the criteria is:
>
> > Must take args as per derived.
> > Must make sense as prefix.
> > Must embrace the equality relation as well.
>
> Based on the criteria and all the discussion I'm now in favor of kind? .
>
> I read about Haskell kinds and I don't think there's much chance of
> confusion--plus anybody who programs in Haskell has self-selected into
> a group of some of the least confusable people on the planet. :-)
>
> Regarding isa?, I think that (isa? 3 Number) being false will always
> be an initial surprise to someone learning about this feature of
> Clojure. At the very least an example like this should appear in the
> documentation explicitly and be explained. One wouldn't have to remain
> confused for long, but it doesn't fit well as English.

I disagree, and gave earlier examples (as did Shawn) in which we use
exactly that English. It's just overloaded, that's all. It seems a
shame that because it is overloaded we can't use it at all.

I agree that, being overloaded, the docs will have to specify the
meaning, and point to instance? for the other meaning. But a
definition stating

(isa? x y)

"returns true if x is equal to y, or x is directly or indirectly
derived from y, either via a Java type inheritance relationship or a
relationship established via derive."

seems free of internal conflict.

> kind? fits
> better:
>
> (kind? 3 3) -> true (3 is a kind of 3)
>

This doesn't work for me, 3 not being a category.

> I just noticed (again) that instance? has the opposite order of
> arguments. In fact, most times when I write an instance? expression, I
> get the order wrong and have to fix it after. I think it's because I'm
> considering (op A B) as the Clojure way of writing (A op B) so I'm
> expecting "A instance (of) ? B" to be the ordering. The current
> ordering is more like ((op A) B) which has a logic to it as well, just
> persistently not what I expect.
>
> I looked up Java's instanceof and found its argument ordering is the
> opposite of instance?.

Being infix, I think it's somewhat apples and oranges. isa? would lead
a sentence, and works similarly prefix. instance? in prefix is
abstract, neither a leader nor a connective, whereas instanceof in
infix is clearly English-like.

> Rich, I know you make these choices
> deliberately. What's the rationale for (instance? c x) rather than
> (instance? x c)? Ease of partial application?

Yes, for partial application. The idea is that it is like ((op A) B)
as you say, and that maybe someday people will stop asking for this?
that? and the-other? predicates, so we don't have to duplicate the
type hierarchy with ? appended. (foo? x) ==> (instance? foo x).

(He says this ducking, having just added class? :)

My thoughts so far:

isa? is self consistent, its only demerit being its overloaded
nature.

Nothing so far has covered the equality case as well.

There is an (acceptable to me) argument order difference with
instance?, depending upon your perspective

Quite often our use of natural language in programming needs to be
qualified, as a precise, unambiguous meaning doesn't fall out of the
English. We may be facing such a case.

Still open for bids,

Rich

Graham Fawcett

unread,
Jul 29, 2008, 9:17:56 AM7/29/08
to clo...@googlegroups.com

Excellent point. I think the fact that we're having a hard time
finding a "good name" for this function is an indication that it is
trying to do too many things.

+1 for (derived?) or (isa?) as a test of derivation only, and making
the multimethod test #(or (= %1 %2) (derived? %1 %2)).

Graham

Rich Hickey

unread,
Jul 29, 2008, 9:38:46 AM7/29/08
to Clojure
Yes, this composite operation needs to be called in multiple places,
and/or tested by the user. Having to repeat even something as simple
as (or (= x y) (derived? x y)) is a recipe for error. Giving the
function a name ensures a single definition and point of maintenance.

Rich

lpetit

unread,
Jul 29, 2008, 10:15:05 AM7/29/08
to Clojure
On 29 juil, 15:38, Rich Hickey <richhic...@gmail.com> wrote:
> > Do I miss something ?
>
> Yes, this composite operation needs to be called in multiple places,
> and/or tested by the user. Having to repeat even something as simple
> as (or (= x y) (derived? x y)) is a recipe for error. Giving the
> function a name ensures a single definition and point of maintenance.

I totally agree with you on that.
It's just haven't seen the cases where user code (and not just clojure
internal code) may benefit from (isa?/derived?) testing for equality
(like (isa? "mystring" mystring")).

If the multiple places are almost confined in clojure internal code,
then maybe the composite operation could just be factored for clojure
internals, and (isa?/derived?) could then stick to strict hierarchy
comparison, throwing an error if the operands neither belong to the
global hierarchy, nor are java types.

If you think user code may benefit from this composite operation as
well, then I'll argue that some other user may still see benefit in a
(derived?) function solely dedicated to test hierarchies.
You could then consider exposing both functions : (derived?), and
(isa?) that is a composition of classical equality (=) + (derived?) ?

Anyway, I think this will be my last contribution in this (overly
long) thread:
I can live with either (isa?) (derived?) (kinda?) (kind-of?) ...
function, as long as the documentation is clear about what it makes,
and what it doesn't make :-)

--
Laurent

Rich Hickey

unread,
Jul 29, 2008, 10:39:45 AM7/29/08
to clo...@googlegroups.com
On Tue, Jul 29, 2008 at 10:15 AM, lpetit <lauren...@gmail.com> wrote:
>
> On 29 juil, 15:38, Rich Hickey <richhic...@gmail.com> wrote:
>> > Do I miss something ?
>>
>> Yes, this composite operation needs to be called in multiple places,
>> and/or tested by the user. Having to repeat even something as simple
>> as (or (= x y) (derived? x y)) is a recipe for error. Giving the
>> function a name ensures a single definition and point of maintenance.
>
> I totally agree with you on that.
> It's just haven't seen the cases where user code (and not just clojure
> internal code) may benefit from (isa?/derived?) testing for equality
> (like (isa? "mystring" mystring")).
>

Well, it's a brand new feature, so any usage predictions are
speculative, but I presume isa? will be used frequently at the Repl
during development in order to test dispatch value matching and
hierarchies before using them in multimethods, and afterwards as a
debugging aid.

Rich

J. McConnell

unread,
Jul 29, 2008, 3:34:13 PM7/29/08
to clo...@googlegroups.com
First off, I'd be happy with isa?, kind-of? or derived? as long as the
docs are clear.

On Tue, Jul 29, 2008 at 9:17 AM, Rich Hickey <richh...@gmail.com> wrote:
> On Jul 29, 8:29 am, "Stephen C. Gilardi" <scgila...@me.com> wrote:
>
> I agree that, being overloaded, the docs will have to specify the
> meaning, and point to instance? for the other meaning. But a
> definition stating

That would be my only request if sticking with isa? (which I think is
a good name both in terms of intent and brevity), that the docs point
to instance?.

>> I just noticed (again) that instance? has the opposite order of
>> arguments.

>> ...


>> Rich, I know you make these choices
>> deliberately. What's the rationale for (instance? c x) rather than
>> (instance? x c)? Ease of partial application?
>
> Yes, for partial application. The idea is that it is like ((op A) B)
> as you say, and that maybe someday people will stop asking for this?
> that? and the-other? predicates, so we don't have to duplicate the
> type hierarchy with ? appended. (foo? x) ==> (instance? foo x).

I had the same question, but the partial application reasoning makes
sense. Still, coming from the Java world, it doesn't matter that the
name is instance? instead of instanceof?, I still read it as the
latter and the argument ordering will trip me up again, I'm sure.
Especially now that isa? has the opposite ordering. I'm not arguing
to change it, now that I know the motivation behind it, but felt it
was worth pointing out that I'm sure this will be confusing for others
as well. Maybe it would be worth a mention in the docs that the
ordering is to enable partial application with a quick example, such
as:

user=> (def collection? (partial instance? java.util.Collection))
#'user/collection?
user=> (collection? '(1 2 3))
true

- J.

Randall R Schulz

unread,
Jul 29, 2008, 5:14:16 PM7/29/08
to clo...@googlegroups.com
On Tuesday 29 July 2008 05:29, Stephen C. Gilardi wrote:
> ...

>
> Based on the criteria and all the discussion I'm now in favor of
> kind? .
>
> ...

>
> Regarding isa?, I think that (isa? 3 Number) being false will always
> be an initial surprise to someone learning about this feature of
> Clojure. At the very least an example like this should appear in the
> documentation explicitly and be explained. One wouldn't have to
> remain confused for long, but it doesn't fit well as English. kind?
> fits better:
>
> (kind? 3 Number) -> false (3 is not a kind of number)
> (kind? Integer Number) -> true (Integer is a kind of number)
> (kind? 3 3) -> true (3 is a kind of 3)

Gack! Non-types are not kinds of anything! Individual numbers (3, e.g.)
are not kinds of things. Why would one want to conflate an equivalence
relation with a type subsumption relation?

I think proper restrictions on argument types is important here.
Whatever it ends up being called, the kind? predicate should accept
only types (Class instances) as its arguments. The individual / type
predicate should accept only a Class instance as its second argument.
Inappropriate arguments should elicit IllegalArgumentException.


> ...
>
> --Steve


Randall Schulz

Chouser

unread,
Jul 29, 2008, 5:52:22 PM7/29/08
to clo...@googlegroups.com
On Tue, Jul 29, 2008 at 5:14 PM, Randall R Schulz <rsc...@sonic.net> wrote:
>
> I think proper restrictions on argument types is important here.
> Whatever it ends up being called, the kind? predicate should accept
> only types (Class instances) as its arguments.

Except that (derive) is specifically meant to work on
non-class-instances, like keywords. So (derives?) (or whatever it's
called) clearly needs to as well.

BTW, in general the "makes partial work well" argument seems pretty weak to me.

(partial instance? Foo)
vs.
#(instance? Foo %)

I realize partial predates the #() syntax, but for discussions of
future syntax, I think #() is worth keeping in mind.

--Chouser

Randall R Schulz

unread,
Jul 29, 2008, 6:46:37 PM7/29/08
to clo...@googlegroups.com
On Tuesday 29 July 2008 14:52, Chouser wrote:

> On Tue, Jul 29, 2008 at 5:14 PM, Randall R Schulz wrote:
> > I think proper restrictions on argument types is important here.
> > Whatever it ends up being called, the kind? predicate should accept
> > only types (Class instances) as its arguments.
>
> Except that (derive) is specifically meant to work on
> non-class-instances, like keywords.

I missed that. The part about it being true when = is true is mostly
what I was responding to.


> So (derives?) (or whatever it's called) clearly needs to as well.

I think I'm not seeing the whole discussion. I have yet to get my copy
of my post (the one to which you replied) and I got a notice from the
Google Groups software a couple of days ago saying they were having
trouble delivering to my mailbox, so I'm afraid I may have lost some
posts.

I suppose I should go to the Web interface and read the thread...


> ...
>
> --Chouser


Randall Schulz

Jeremy Shoemaker

unread,
Jul 29, 2008, 3:48:59 PM7/29/08
to clo...@googlegroups.com
On Tue, Jul 29, 2008 at 2:34 PM, J. McConnell <jdo...@gmail.com> wrote:

>
> user=> (def collection? (partial instance? java.util.Collection))
> #'user/collection?
> user=> (collection? '(1 2 3))
> true
>

Suppose the argument order was the other way around, what's to keep you from just
doing this?

  user=> (defn collection? [x] (instance? x java.util.Collection))

  #'user/collection?
  user=> (collection? '(1 2 3))
  true

Does partial do something extra that I'm not aware of?  I am kinda new to
Clojure, so I could be wrong.

Jeremy

lpetit

unread,
Jul 30, 2008, 5:08:09 AM7/30/08
to Clojure
On 30 juil, 00:46, Randall R Schulz <rsch...@sonic.net> wrote:
> On Tuesday 29 July 2008 14:52, Chouser wrote:
>
> > On Tue, Jul 29, 2008 at 5:14 PM, Randall R Schulz wrote:
> > > I think proper restrictions on argument types is important here.
> > > Whatever it ends up being called, the kind? predicate should accept
> > > only types (Class instances) as its arguments.
>
> > Except that (derive) is specifically meant to work on
> > non-class-instances, like keywords.
>
> I missed that. The part about it being true when = is true is mostly
> what I was responding to.
>
> > So (derives?) (or whatever it's called) clearly needs to as well.

Well, I think the concrete value or value type (keyword, string, ...)
used in the call to (isa?/derived?) is less important that the fact
that this value/value type is already in the value domain of (union
(java class) (java interface) (global hierarchy collection introduced
by Rich -to which you add new members via (hierarchy ...)).

--
Laurent

J. McConnell

unread,
Jul 30, 2008, 11:21:34 AM7/30/08
to clo...@googlegroups.com
On Tue, Jul 29, 2008 at 3:48 PM, Jeremy Shoemaker <ceci...@gmail.com> wrote:
> On Tue, Jul 29, 2008 at 2:34 PM, J. McConnell <jdo...@gmail.com> wrote:
>
>> user=> (def collection? (partial instance? java.util.Collection))
>> #'user/collection?
>> user=> (collection? '(1 2 3))
>> true
>
> Suppose the argument order was the other way around, what's to keep you from
> just
> doing this?
>
> user=> (defn collection? [x] (instance? x java.util.Collection))
> #'user/collection?
> user=> (collection? '(1 2 3))
> true

Nothing, nor just using the anonymous function literal #(instance?
java.util.Collection %), like Chouser suggested. This was the first
time I'd seen partial, so I was grokking it as I was writing this.
However, now I'm back in the camp that thinks instance?'s argument
order should be swapped, if possible.

- J.

Randall R Schulz

unread,
Jul 30, 2008, 11:33:59 AM7/30/08
to clo...@googlegroups.com
On Tuesday 29 July 2008 07:15, lpetit wrote:
> On 29 juil, 15:38, Rich Hickey <richhic...@gmail.com> wrote:
> > > Do I miss something ?
> >
> > Yes, this composite operation needs to be called in multiple
> > places, and/or tested by the user. Having to repeat even something
> > as simple as (or (= x y) (derived? x y)) is a recipe for error.
> > Giving the function a name ensures a single definition and point of
> > maintenance.
>
> ...

> It's just haven't seen the cases where user code (and not just
> clojure internal code) may benefit from (isa?/derived?) testing for
> equality (like (isa? "mystring" mystring")).

I agree. I think it's a mistake to combine what are really two radically
different predicates / relations into a single function unless the name
itself discloses the hybrid nature of the test it encodes.


> ...
>
> --
> Laurent


Randall Schulz

Graham Fawcett

unread,
Jul 30, 2008, 12:12:20 PM7/30/08
to clo...@googlegroups.com
On Tue, Jul 29, 2008 at 3:48 PM, Jeremy Shoemaker <ceci...@gmail.com> wrote:

No, your point is reasonable, IMO. For a two-argument function, the
case for partial evaluation isn't strong, especially when Clojure has
an excellent shorthand for anonymous functions -- #(instance?
java.util.Collection %) is arguably easier to read than (partial
instance? java.util.Collection).

Partials are great for functions with many arguments (or arglists of
variable length). In a language such as Haskell, where partial
evaluation is commonplace and the syntax for writing a partial is
cleaner than the syntax for an anonymous function, there are more
compelling reasons to use them for functions with only a few
arguments, IMO.

Having said all of that, I'd still vote for leaving the arguments in a
partial-evaluation-friendly order, unless there's a strong reason not
to. If nothing else, it encourages a style of coding common in
functional languages, and I think that's a good thing for Clojure
to promote.

Best,
Graham

Rich Hickey

unread,
Jul 30, 2008, 12:21:25 PM7/30/08
to Clojure


On Jul 28, 10:08 am, Rich Hickey <richhic...@gmail.com> wrote:
>
> At this early stage, I'm open to alternative names for isa?

I've decided to keep isa? - I'll finesse the docs soon.

Changes to instance? are not on the table.

Thanks to all for the interesting discussion on names, I hope we can
have as interesting a discussion on a la carte hierarchies and
multimethod polymorphism.

Rich
Reply all
Reply to author
Forward
0 new messages