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:
;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.
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.
> ;(= 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:
On Sun, Jul 27, 2008 at 10:12 PM, Rich Hickey <richhic...@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.
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?
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?
> 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.
> 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.
> 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.
> > At this early stage, I'm open to alternative names for isa?
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.
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:
> 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}
> ;(= 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:
> ;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.
> 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.
On Mon, Jul 28, 2008 at 12:02 PM, Rich Hickey <richhic...@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.
> On Mon, Jul 28, 2008 at 12:02 PM, Rich Hickey <richhic...@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.
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.
On Mon, Jul 28, 2008 at 12:53 PM, Rich Hickey <richhic...@gmail.com> wrote:
> On Jul 28, 12:18 pm, "Graham Fawcett" <graham.fawc...@gmail.com> > wrote: >> On Mon, Jul 28, 2008 at 12:02 PM, Rich Hickey <richhic...@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.
> 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.
> 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?
"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:
> > 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?
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?
> "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?".
> > >> > ;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?
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.
(is? ...) seems wrong to me -- it suggests an identity comparison.
> 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.
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?) ?
On Mon, Jul 28, 2008 at 6: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.
> What about is? - seems to imply the instanceof relationship less than > 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
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 :).
> On Mon, Jul 28, 2008 at 6: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.
> What about is? - seems to imply the instanceof relationship less than > 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
> 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?
> > 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.
On Mon, Jul 28, 2008 at 11:13 PM, lpetit <laurent.pe...@gmail.com> wrote:
> 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?) ?
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)).
> 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 ?
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.
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 :-)