Hi, In CLOS, can I overload the function equal so that it can compare two objects? If I try it on CMUCL 18a with PCL, on Linux, I get:
* ;;;Evaluating defmethod equalp Error in function PCL::GENERIC-CLOBBERS-FUNCTION: EQUALP already names an ordinary function or a macro, you may want to replace it with a generic function, but doing so will require that you decide what to do with the existing function definition. The PCL-specific function MAKE-SPECIALIZABLE may be useful to you.
Restarts: 0: [ABORT] Return to Top-Level.
Debug (type H for help)
(PCL::GENERIC-CLOBBERS-FUNCTION EQUALP)
Is this possible? Or should I write some object specific function, just like for a structure?
Thank you Mani.
-----== Posted via Deja News, The Leader in Internet Discussion ==----- http://www.dejanews.com/ Now offering spam-free web-based newsreading
ee95...@ee.iitm.ernet.in writes: > Hi, In CLOS, can I overload the function equal so that it can compare two > objects?
EQUAL and EQUALP are already defined for two (standard) objects. Both are defined to use EQ on standard objects. As such, you would not be "overloading", you would instead be "redefining", and this is forbidden by the standard except for operations that are specifically documented as customizable generics. (The constraint is in the conceptual matter on packages, where it talks about constraints of users and implementors on symbols defined in the COMMON-LISP package.)
The error message you're getting for your DEFMETHOD of EQUALP in CMU/PCL is telling you that the version of EQUALP used there is not even implemented as a generic function, so attempting to add methods doesn't make sense.
what Kent said, and in addition, just that it's a bad idea to overload those primitives, like EQUAL. With so many names out there, I would like to know why this would be necessary. furthermore, since EQUAL takes 2 objects, this is hard to understand.
I've never "advised" a function in CL, though I do it all the time in elisp, but I do know that you can do something like this in CL (see `defadvice', I think). So maybe you can modify the behavior of EQUAL without having to redefine the whole function. it might be worth looking into it.
In article <cxjemwezc4w....@hawk.bu.edu>, David Bakhash <ca...@bu.edu> writes:
> what Kent said, and in addition, just that it's a bad idea to overload > those primitives, like EQUAL. With so many names out there, I would > like to know why this would be necessary. furthermore, since EQUAL > takes 2 objects, this is hard to understand.
> I've never "advised" a function in CL, though I do it all the time in > elisp, but I do know that you can do something like this in CL (see > `defadvice', I think). So maybe you can modify the behavior of EQUAL > without having to redefine the whole function. it might be worth > looking into it.
> dave
Be careful advising routines in the common-lisp package. I once advised NTH on my lisp machine and was power cycling the machine a few minutes later!
In article <cxjemwezc4w....@hawk.bu.edu>, David Bakhash <ca...@bu.edu> wrote:
> what Kent said, and in addition, just that it's a bad idea to overload > those primitives, like EQUAL. With so many names out there, I would > like to know why this would be necessary. furthermore, since EQUAL > takes 2 objects, this is hard to understand.
Well, I have a custom compare function. It's just that I thought if I could extend the EQUAL function, it would seem more intuitive. Guess it's not, for you at least :-)
> I've never "advised" a function in CL, though I do it all the time in > elisp, but I do know that you can do something like this in CL (see > `defadvice', I think). So maybe you can modify the behavior of EQUAL > without having to redefine the whole function. it might be worth > looking into it.
> dave
I've used defadvice in elisp, but never in lisp (just started with lisp). I'll give it a try.
Thanks anyway, Mani.
-----== Posted via Deja News, The Leader in Internet Discussion ==----- http://www.dejanews.com/ Now offering spam-free web-based newsreading
* ee95...@ee.iitm.ernet.in | Hi, In CLOS, can I overload the function equal so that it can compare two | objects? ... Or should I write some object specific function, just like | for a structure?
actually, both answers should be "yes", despite the also correct answer "no" to the first question -- you just have to do it somewhat differently from what the question implies you would do it, and probably from what most people would recommend. however, to show that it can be done, here's a snippet of code that should illustrate the point.
the key is to use the package system so you effectively define a _new_ function that is known as EQUAL in your package, but which differs from the EQUAL function that is in the COMMON-LISP package.
(defpackage :mani (:use :common-lisp) ;and whatever else you need (:shadow 'equal))
(in-package :mani)
(defclass mani-class ...whatever...)
(defgeneric equal (object1 object2) "true if OBJECT1 and OBJECT2 are in some sense the same.")
(defmethod equal ((object1 t) (object2 t)) "the standard equality predicate" (cl:equal object1 object2))
(defmethod equal ((object1 mani-class) (object2 mani-class)) "the specialized predicate for MANI-CLASS objects" ...whatever...)
at this point, EQUAL will work like you would expect it to work, but in some cases, you may get confusing results. e.g., #'EQUAL is no longer EQ to #'CL:EQUAL, and some functions may test for the functional value of the EQUAL symbol in the COMMON-LISP package explicitly. this may alone be sufficient reason _not_ to recommend this way of solving the problem.
#:Erik -- http://www.naggum.no/spam.html is about my spam protection scheme and how to guarantee that you reach me. in brief: if you reply to a news article of mine, be sure to include an In-Reply-To or References header with the message-ID of that message in it. otherwise, you need to read that page.
Erik Naggum <cle...@naggum.no> writes: > * ee95...@ee.iitm.ernet.in > | Hi, In CLOS, can I overload the function equal
> the key is to use the package system so you effectively define a _new_ > function that is known as EQUAL in your package, but which differs from > the EQUAL function that is in the COMMON-LISP package.
But no amount of doing this will make CL:EQUAL (a recursive function) call back to MY:EQUAL when it finds one of my objects on a recursion.
And whoever asked a while ago about writing one of their own was right on one point--we don't provide the capability to duplicate the descent of structure objects since with a MOP (which most implementations have, but not all, and whose interface varies slightly), there's no way to enumerate the slot names to descend using just ANSI CL.
So the problem isn't just what's called "package dyslexia" (confusing CL:EQUAL with MY:EQUAL) but more fundamentally the identity problem for recursive functions. CL should have had an EQUAL that took keyargs like the sequence functions, but since it doesn't, one pretty much takes what is given or only approximately constructs something similar.
On Sat, 27 Jun 1998 22:48:03 GMT, Kent M Pitman <pit...@world.std.com> wrote:
(...)
>So the problem isn't just what's called "package dyslexia" (confusing >CL:EQUAL with MY:EQUAL) but more fundamentally the identity problem for >recursive functions. CL should have had an EQUAL that took keyargs like >the sequence functions, but since it doesn't, one pretty much takes >what is given or only approximately constructs something similar.
I have wondered several times if it would be reasonable to introduce a *generic* function called something like OBJECT-EQUAL (or EQUAL-OBJECT?) which would have methods to deal with the standardised classes (or a well-chosen sample thereof), and for which the user would be allowed to add new methods for their own classes and perhaps to replace the implementation's methods for standardised classes. (Should this go together with an *OBJECT-EQUAL* hook?)
EQ, EQL, EQUAL, and EQUALP are best left as they are, in my opinion. It is well emphasised that they are not perfect and not the answer to everything.
Perhaps having a generic and hence user-extensible equality predicate has already been discussed (by the standardisation committee?), but I don't know the outcome of this discussion.
In article <19980629180901.10930.qm...@nym.alias.net>,
Vassil Nikolov <vniko...@math.acad.bg> wrote: >I have wondered several times if it would be reasonable >to introduce a *generic* function called something like >OBJECT-EQUAL (or EQUAL-OBJECT?) which would have methods >to deal with the standardised classes (or a well-chosen >sample thereof), and for which the user would be allowed >to add new methods for their own classes and perhaps to >replace the implementation's methods for standardised >classes. (Should this go together with an *OBJECT-EQUAL* >hook?)
I once wrote a specification for such a thing (and a similar COPY-OBJECT) about 8 years ago, with the plan of publishing it in Lisp Pointers, but never got around to polishing it up sufficiently. It included keyword arguments that allowed you to specify whether they should do a deep or shallow test/copy, and what they should do when they get to a type that uses the default method (since users would not be able to define methods on the standardizes classes). It ended up being pretty complex, because it needed to be able to cover so many bases. This is because the notions of equality and copying are not just dependent on the type of data, but on how you're using them. For instance, a cons can be considered part of the backbone of a list, in which case you may just want a shallow comparison of all the corresponding cars, or as the root of a tree, in which case you want a deep comparison that stops at the leaves. Perhaps user-defined types wouldn't be as overloaded as cons cells are, but I don't think it would be appropriate to design a general facility with such an assumption.
-- Barry Margolin, bar...@bbnplanet.com GTE Internetworking, Powered by BBN, Cambridge, MA *** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Barry Margolin <bar...@bbnplanet.com> writes: > >I have wondered several times if it would be reasonable > >to introduce a *generic* function called something like > >OBJECT-EQUAL (or EQUAL-OBJECT?) which would have methods > >to deal with the standardised classes (or a well-chosen > >sample thereof), and for which the user would be allowed > >to add new methods for their own classes and perhaps to > >replace the implementation's methods for standardised > >classes. (Should this go together with an *OBJECT-EQUAL* > >hook?)
> I once wrote a specification for such a thing (and a similar COPY-OBJECT) > about 8 years ago, with the plan of publishing it in Lisp Pointers, but > never got around to polishing it up sufficiently. It included keyword > arguments that allowed you to specify whether they should do a deep or > shallow test/copy, and what they should do when they get to a type that > uses the default method (since users would not be able to define methods on > the standardizes classes). It ended up being pretty complex, because it > needed to be able to cover so many bases. This is because the notions of > equality and copying are not just dependent on the type of data, but on how > you're using them. For instance, a cons can be considered part of the > backbone of a list, in which case you may just want a shallow comparison of > all the corresponding cars, or as the root of a tree, in which case you > want a deep comparison that stops at the leaves. Perhaps user-defined > types wouldn't be as overloaded as cons cells are, but I don't think it > would be appropriate to design a general facility with such an assumption.
This is one of the reasons I keep saying the problem with all of these is that they don't deal with the notion of "intention". See my Lisp Pointers article at http://world.std.com/~pitman/PS/EQUAL.html for an explanation of how that plays in.
But another point sort of implied by Barry and not directly made by my paper is this: It is a BAD BAD BAD BAD BAD idea to make a generic function solely for the purpose of letting people add methods that they like. One should make generic functions always and only for the purpose of implementing a truly generic behavior. And although it's tricky to define this in formal terms, what I mean informally is that you must be able to describe unambiguously what it means to use the generic function such that any implementor will make a consistent defintion. This is the fundamental difference between "genericity" and "overloading" and is very hard to enforce mechanically. So, for example, just making the function + be generic is not good enough. One is always at risk of someone defining "ABC" + "DEF" => "ABCDEF" when it seems to me that the answer "18AB" would be at least as defensible (think base-16). Adding "string concatenation" to a function that does "arithmetic addition" is overloading, not specializing.
So when one says OBJECT-EQUAL, what does one mean. EQL, EQUAL, and EQUALP aren't just faster and slower versions of the same thing. They return true and false for REASONS, and one calls them because they have a situation in which that reason matters. For example, EQ is mostly useless as a hash-table test for a case-insensitive table of strings. In that case, EQUALP is right. But (EQUALP x y) is wrong if you're asking "will a side-effect to X affect Y?" So you have to ask: What question is OBJECT-EQUAL answering? Because in fact, either of EQUAL, or EQUALP *might* in principle want to be customized. We don't let you customize them partly becuase we don't know what the abstract description of them is--we know they're used as blunt tools by many. So we DEFINE them to mean what they do, not what they are trying to achieve. That is the very antithesis of genericity, even though it's available for multiple datatypes.
It's easy to prove that OBJECT-EQUAL is NOT a generic version of EQUAL or EQUALP because those functions are DEFINED to work a certain way on your data already. Any method you write that deviates has a different answer and is therefore violating the definition of EQUAL or EQUALP. So what does OBJECT-EQUAL compute? (This is the issue of intention that Barry raised.) Because, for example, (OBJECT-EQUAL me you) might want to be true if you were trying to find a substitute for yourself in a chess game and we had about the same chess-playing capabilities, but (OBJECT-EQUAL me you) might be false if you were looking for someone to substitute for you as a typist and I typed much more slowly than you. No single predicate can satisfy all possible needs, so you have to plainly specify what you're going to use the objects for in order for someone writing custom methods to know what to do.
The reason this is not normally something people think about, I think, is that in any given application, it's often a given. You make a definition that "works for your need". But while this works in the nearterm, it makes for lousy "code re-use". Because if needs change, the predicate will not seem to behave as well. And if someone tries to layer another application on yours and calls OBJECT-EQUAL for their own purpose on an object containing one of your objects, you may return false for a reason the other guy considers unimportant but since only one predicate is involved where two may have been better, you end up losing...
Well, I don't expect everyone agrees with me, but then, (OBJECT-EQUAL #<your-idea> #<my-idea>) should return what? (Are you sure of the constancy of your answer for all purposes?)
> This is one of the reasons I keep saying the problem with all of these > is that they don't deal with the notion of "intention". See my Lisp > Pointers article at http://world.std.com/~pitman/PS/EQUAL.html > for an explanation of how that plays in.
> ...
> The reason this is not normally something people think about, I think, > is that in any given application, it's often a given. You make > a definition that "works for your need". But while this works > in the nearterm, it makes for lousy "code re-use". Because if needs > change, the predicate will not seem to behave as well. And if someone > tries to layer another application on yours and calls OBJECT-EQUAL > for their own purpose on an object containing one of your objects, > you may return false for a reason the other guy considers unimportant > but since only one predicate is involved where two may have been > better, you end up losing...
> Well, I don't expect everyone agrees with me, but then, > (OBJECT-EQUAL #<your-idea> #<my-idea>) > should return what? (Are you sure of the constancy of > your answer for all purposes?)
Perhaps this is as simple as adding a tag for the intention. For example, I could imagine an additional keyword, :wrt ("with respect to") that declared the intention. Then,
fitzger...@neodesic.com writes: > In article <sfw67hjac5s....@world.std.com>, > Kent M Pitman <pit...@world.std.com> wrote:
> > This is one of the reasons I keep saying the problem with all of these > > is that they don't deal with the notion of "intention". See my Lisp > > Pointers article at http://world.std.com/~pitman/PS/EQUAL.html > > for an explanation of how that plays in. > > ... > > Well, I don't expect everyone agrees with me, but then, > > (OBJECT-EQUAL #<your-idea> #<my-idea>) > > should return what? (Are you sure of the constancy of > > your answer for all purposes?)
> Perhaps this is as simple as adding a tag for the intention. For example, I > could imagine an additional keyword, :wrt ("with respect to") that declared > the intention. Then,
Well, my paper mostly makes this contention, but here are a couple of things to note.
First, is that it's an artifact of CLOS that you can't dispatch off of optional or keyword args. So you'd want to make it required. You'd want to include it always anyway since there's no universallyy agreed upon "reasonable default".
Second, it's worth at least mentioning that if the person is going to potentially have to write a different method for each intentional type, this is not operationally a lot different than having myriad different functions (though perhaps it makes them easier to find and easier to allow to have default definitions). My only real point here is that this is confirming evidence of what I said before that there is no uniquely defined function for equality; the idea of "fixing" that by adding an argument is just a "packaging trick".
Third, and I've said this before, it's a bad idea to use the KEYWORD datatype in an "extensible" situation. Better to use packaged symbols to avoid name conflicts from non-cooperating systems because separation of their chosen packages will likely protect you from them clobbering each others' definitions.
> I can also imagine standard :wrt forms (for example, :shallow-container > or :deep-container) that are implemented in standard ways.
You'd think. But let me underscore my previous remark. It takes more than a pretty name to really specify the semantics of one of these keywords. You have to make it clear enough that people can resolve ambiguities in a straightfoward and predictable way. For example, (OBJECT-EQUAL '(A (B C)) '(A (B C)) 'DEEP-CONTAINER) might seem to work, but consider (OBJECT-EQUAL '(A (B #(C D))) '(A (B #(C D))) 'DEEP-CONTAINER) I claim that there are two entirely reasonable interpretations here, and that the english words "deep" and "container" are insufficient to resolve this. And what about (OBJECT-EQUAL '(A #<REFRIGERATOR-1 empty>) '(A #<REFRIGERATOR-2 empty>) 'DEEP-CONTAINER) or (OBJECT-EQUAL '(#\A #\B) "AB" 'DEEP-CONTAINER) I prefer generics get a real desription. Of course, the problem is not the choice of name per se, since some name must be chosen, but the fact that the name is not enough. So if you write:
DEEP-CONTAINER -- Describes a storage mode built of possibly-nested containing structures where the identity and type of the containers is not significant, and where nested unlike containers are still considered part of the same deep container.
then I would expect (OBJECT-EQUAL '(#\A #\B) "AB" 'DEEP-CONTAINER) => T (OBJECT-EQUAL '(A #<REFRIGERATOR-1 empty>) '(A #<REFRIGERATOR-2 empty>) 'DEEP-CONTAINER) => T (OBJECT-EQUAL '#<REFRIGERATOR contents: (A NIL)> '(A #<REFRIGERATOR-2 empty>) 'DEEP-CONTAINER) => T
assuming that there was some abstract or real predicate CONTAINER-P what we all agreed yields true for lists, refrigerators, arrays (and strings), and yields false for characters and symbols.
But there could be an alternate DEEP-HOMOGENOUS-CONTAINER that allows only nesting of objects of the same type, and uses EQL at the edges but that still doesn't care about the container type for other purposes. So:
(OBJECT-EQUAL '#<PAPER-BAG contents (#<MILK-BOTTLE-1 empty>)> '#<PLASTIC-BAG contents (#<MILK-BOTTLE-1 empty>)>) might return T but (OBJECT-EQUAL '#<PAPER-BAG contents (#<MILK-BOTTLE-1 empty)>)> '#<PLASTIC-BAG contents (#<MILK-BOTTLE-2 empty>)>) because EQL of two distinct milk bottles, even though they were containers, might return NIL. Or DEEP-HOMOGENOUS-TYPE-SENSITIVE-CONTAINER that requires the same kind of container to be used in comparisons. Well, you can easily see that for every parameter of comparison, there is potentially a new predicate. And I'm not sure a generic OBJECT-EQUAL is the right way to express this, even though it's capable. Maybe it is. But I'm not sure.
If anyone still thinks object equality is simpler than I'm making it out to be, imagine going to an insurance policy you own and finding some item that is insured and will be replaced with an "equal" one (e.g., your car?), and ask yourself what the likelihood is that you and the insurance company will agree on the meaning of the word equal.
One of the truly great problems in the world today, extending well beyond computer science, is people's lack of understanding of how non-specific the language they use for communication is. They assume that when they say a neat and tidy word with a great deal of precision and that someone else says they're doing the same, that each is using the word the same way. But word definitions are not so clear. When I see a "pretty sunset" I might mean "varying widely in color" while you might look at the same sunset and agree with me it was pretty because it "swirls in complex ways". And we are oh-so-happy that another appreciates what we do. We never think to challenge people who agree with us. But oh how angry we get when the same situation happens and we think we're disagreeing. When we may not be disagreeing at all--just using bad terminology. As often as not, it seems to me, people are fighting over claim to use of a certain simple word as anything else.
* Kent M Pitman | One of the truly great problems in the world today, extending well beyond | computer science, is people's lack of understanding of how non-specific | the language they use for communication is. ... We never think to | challenge people who agree with us.
well, I do. I don't _want_ people to agree with me -- I want them to understand, and then it's usually immaterial whether they agree with me or not, not only because it's understanding I see, but also because we will each have modified our positions while getting to understand each other, however slightly, so there's no "winning" aspect to such agreeing. it turns out that those who understand the least are the ones most eager to agree, or _claim_ that they agree with others, or, in one particularly annoying case, claim that _others_ agree with him, making a double error.
agreement is useful among people who understand eachother. since most people are incapable of seeing when such understanding is absent, as you point out, it is quite a telling indicator when they insist on agreeing despite slowly mounting evidence of lack of (common) understanding.
wide-area communication as o the Internet and USENET highlights the need to question the desire to agree before understanding is established. in my experience, this particular behavioral pattern is usually restricted to people who use agreement as a means of approval from their peers, no matter whether it is a real or imagined agreement. you find the same in gangs and in politics and everywhere people with low self-esteem need to affirm themselves at any cost.
real, as in fundamental, agreement is remarkably rare. it's like the holy grail, true love, etc. ... and those who call every fuzzy string match an "agreement" are likely never to find _true_ agreement, precisely because it requires so much effort from those involved. like everything else in our time, people want it to be easy and user-friendly. agreeing with somebody is _really_ hard, and if it comes _too_ easy, you know it must be from dishonesty and false premises. then it becomes demeaning.
#:Erik -- http://www.naggum.no/spam.html is about my spam protection scheme and how to guarantee that you reach me. in brief: if you reply to a news article of mine, be sure to include an In-Reply-To or References header with the message-ID of that message in it. otherwise, you need to read that page.
In article <sfwk95yoqfo....@world.std.com>, Kent M Pitman
<pit...@world.std.com> wrote: > One of the truly great problems in the world today, extending well beyond > computer science, is people's lack of understanding of how non-specific > the language they use for communication is. They assume that when they > say a neat and tidy word with a great deal of precision and that someone > else says they're doing the same, that each is using the word the same > way. But word definitions are not so clear. When I see a "pretty sunset" > I might mean "varying widely in color" while you might look at the same > sunset and agree with me it was pretty because it "swirls in complex ways". > And we are oh-so-happy that another appreciates what we do. We never > think to challenge people who agree with us. But oh how angry we get > when the same situation happens and we think we're disagreeing. When we > may not be disagreeing at all--just using bad terminology. As often as > not, it seems to me, people are fighting over claim to use of a certain > simple word as anything else.
This is very very true. These issues come into much clearer focus when you're developing systems that try to use natural language. For us, we need to deal not only with ontological issues, but those of epistemology. We're lucky in that we get to define these with respect to our own needs; I shudder to think what would happen if we needed to "play well with others", e.g. see Ontolingua.
In article <sfwk95yoqfo....@world.std.com>, Kent M Pitman
<pit...@world.std.com> wrote: > One of the truly great problems in the world today, extending well beyond > computer science, is people's lack of understanding of how non-specific > the language they use for communication is. They assume that when they > say a neat and tidy word with a great deal of precision and that someone > else says they're doing the same, that each is using the word the same > way. But word definitions are not so clear.
This was a favorite topic of the Anthropologist Tony F.C. Wallace a while back. His whole thesis was that political and social organizations *require* such vagueness, because if we were really precise about our interests we'd rarely find common cause.
Alternatively, to turn this on it's head, one of the great problems of computer science is getting machines to act more like people in this respect.
Raf
-- Raffael Cavallaro
This message made with 100% recycled electrons, 40% post-consumer.
In article <3108222109765...@naggum.no>, Erik Naggum <cle...@naggum.no> wrote: > * Kent M Pitman > | One of the truly great problems in the world today, extending well beyond > | computer science, is people's lack of understanding of how non-specific > | the language they use for communication is. ... We never think to > | challenge people who agree with us.
> well, I do. I don't _want_ people to agree with me --
this is abundantly clear from your posts on comp.lang.lisp ;^)
Raf (just kidding!)
-- Raffael Cavallaro
This message made with 100% recycled electrons, 40% post-consumer.
I agree that an extensible equality protocol which is all things to all people is hard to build. However, a less ambitious equality protocol can be a good peg on which to hang useful default behaviour.
In languages with infix operator syntax (like Dylan) it is convenient to be able to write:
if (x = y) ...
instead of:
if ( equal-in-the-obvious-way?(x, y) ) ...
Clearly, however, the type-definer has to document what he or she means by =, but this doesn't prevent them from making other predicates available.
For example, the CORBA standard defines an equality operation, IS_EQUIVALENT, on two object references which determines whether these two (proxies) do in fact refer to the same logical object. It is completely natural and intuitive in the CORBA Dylan world to define "=" on object references to be equivalent to IS_EQUIVALENT.
Just because something is hard to get completely right for all imaginable cases, doesn't mean it is completely wrong: I used to have a colleague who ridiculed Prolog because it was not totally logical and so used C instead. D'oh!
ja...@harlequin.com (Jason Trenouth) writes: > In languages with infix operator syntax (like Dylan) it is convenient to be > able to write:
> if (x = y) ...
> instead of:
> if ( equal-in-the-obvious-way?(x, y) ) ...
I suggest that there is little experience in whether this really composes well, first of all. The problems I'm talking about don't happen in first-order systems. They happen when two systems of unrelated design incorporate each others data structures and try to interact. Further, I claim that the incidence rate of problems may also be hard to see and this may simply be the source of complex and hard to track down at that point. So I don't expect much empirical evidence to come to the surface nor do I accept the above remark as empirical evidence (except in the obvious sense that this conversation started, which is that it is empirically obvious that everyone but me and a small handful of philosophical holdouts wants to write something short because they consider this a non-issue.
This question may well be one of those things that divides scientists and theorists and philosophers from engineers. After all, if it mostly works, is that so bad? Especially when what is proposed to always work is so much harder to think about that people may make OTHER design or implementation errors in managing that?
In the end, most of what I object to in the above example is not the ability to do that, it's ONLY the choice of the word "obvious". If you said equal-in-some-arbitrary-and-idiosyncratic-and-ill-speicifed-way? and wanted to shorten THAT to = in Dylan or OBJECT-EQUAL in Lisp, I'd have no objection. I don't mind people being arbitrary and idiosyncratic. I mind them not knowing it. Because it causes them to ask a lot of unreasonable questions like "why isn't this in the language?" because they think "obvious" things belong in the language.
> Clearly, however, the type-definer has to document what he or she means by =,
I don't think this is clear at all. Moreover, I do *not* think it is the job of the TYPE definer or else different types will have different contracts and you will have lost genericity. That's what I think Dylan has done--lost genericity. It has only overloading on this operator, which it has offered up as basically "semantically void and available for user definition" and heaven help a pair of uncooperating Dylan modules that share their incompatible uses.
> but this doesn't prevent them from making other predicates available.
Indeed. As I suspect thy ultimately will.
> For example, the CORBA standard defines an equality operation, IS_EQUIVALENT, > on two object references which determines whether these two (proxies) do in > fact refer to the same logical object. It is completely natural and intuitive > in the CORBA Dylan world to define "=" on object references to be equivalent > to IS_EQUIVALENT.
So CORBA really means "EQL" in the Lisp world. That's the easy one because it involves no recursive descent. But Dylan doesn't require this meaning, and couldn't enforce it if it did, so no matter how natural and intuitive it is, it's not what Dylan's EQUAL means.
> Just because something is hard to get completely right for all imaginable > cases, doesn't mean it is completely wrong: I used to have a colleague who > ridiculed Prolog because it was not totally logical and so used C instead. > D'oh!
I'm trying to avoid uses of right and wrong. And I fully well agree that "useful" has its place. It's one of the reasons I prefer to use Lisp rather than Scheme. Again, my issue here is not about "what people choose to do for an engineering task" it's about "whether people understand that they are asking for trouble and are consciously accepting that risk". Just because it is hard to think about the situations where the breakdown occurs doesn't mean one can't know they are there. And just because one knows they are there doesn't mean one can't accept their risk. But it's bad practice, I think, to insist that because they are hard to think about or unlikely to occur that they are "non-issues", "not a problem", etc. The secret to reading a good mystery and guessing the ending before the end is not to discard the clues as uninteresting. Rather, you see them and say "this clue cannot yet be used". And then one day, as Sherlock Holmes would say, it becomes necessary to sweep aside the impossible in order to see the possible. And at that point, the clues to possible bad behavior that you have carefully carried with you all those years suddenly fly into play and you say "I know why this program isn't working. I bet there are incompatible = protocols in play. Kent Pitman always warned me about this and though I insisted he was mostly just wasting my time, I'm glad I at least remembered the content of what he said in case I later turned out to be wrong." ;-)
> And whoever asked a while ago about writing one of their own was right > on one point--we don't provide the capability to duplicate the descent > of structure objects since with a MOP (which most implementations > have, but not all, and whose interface varies slightly), there's no > way to enumerate the slot names to descend using just ANSI CL.
Which is really annoying.
I'd like to see us define an interface for this functionality and then try to implement it in every Common Lisp in which it's possible to do so.
In this, it's important not to let the best be the enemy of the good. We should not let our desire to do things right stop us from doing something useful.
I already know there are a number of principled objections that may come up. But let's not let them stop us.
And if we can't do everything we'd like to do, let's at least try to do some of it.
To start things off, here's a straw porposal:
I want to be able to:
1. look at the value of every slot. 2. make a copy of the object with each slot's value replaced by a function of that value. 3. modify all the slots rather than make a copy.
So:
(walk-slots fn object)
Like mapc. Calls fn on each slot value, discarding the results, and returns the object.
(map-slots fn object)
Like mapcar. Makes a copy of the object with each slot value is replaced by the result of calling fn on the value. Returns the object.
(map-into-slots fn object)
Like map-into. Similar to map-slots but replaces the values in the existing object rather than in a copy. Returns the object.
Complaints:
C1: But fn doesn't know which slot it's looking at.
True. Perhaps fn should be called on two args: the slot name and the slot value. We may have to somehow accomodate Lisps in which the slot names are not always available.
C2: Is it suppose to work for *any* object, e.g. conses?
Probably not, but ... the walk/map functions can be generic.
C3: Generic, you say. But then wouldn't it be better for the object arg to be the first, as in (walk-slots object fn)?
Maybe, though on can of course specialize the 2nd arg of a method rather than the 1st.
C4: Doesn't this violate modularity? Maybe I don't want you looking at the insides of my objects.
Maybe I need to do it anyway. But, ok, perhaps we should say instead that it doesn't have to be the case that all slots are visible. Rules of various sorts might be devised. In any case, the walk/map functions can be generic. Map-slots can secretly copy over any values that aren't supposed to be seen. Indeed, map-slots can say "get lost" if copies aren't allowed. But the default behaviour for instances of standard and structure classes should be to make things visible.
C5: What about typed slots?
What about 'em? Everything should still work.
C6: What's really part of an object anyway?
Did I mention that the walk/map functions could be generic? Let's not give in to objections that amount to saying that the whole thing is too hard conceptually, and so we should give up and do nothing.
In article <x2ww9xhbvv.fsf...@gairsay.aiai.ed.ac.uk>, Jeff Dalton <j...@gairsay.aiai.ed.ac.uk> wrote:
>I'd like to see us define an interface for this functionality >and then try to implement it in every Common Lisp in which it's >possible to do so.
>In this, it's important not to let the best be the enemy of the good. >We should not let our desire to do things right stop us from doing >something useful.
Are you saying that this should be independent of the MOP, which already provides this interface?
-- Barry Margolin, bar...@bbnplanet.com GTE Internetworking, Powered by BBN, Cambridge, MA *** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
> It's easy to prove that OBJECT-EQUAL is NOT a generic version of > EQUAL or EQUALP because those functions are DEFINED to work a certain > way on your data already. Any method you write that deviates has > a different answer and is therefore violating the definition of > EQUAL or EQUALP. So what does OBJECT-EQUAL compute? (This is the > issue of intention that Barry raised.) [...]
You're right, but what should we therefore do? Java has a generic "equals" and Common Lisp doesn't, and I know which answer I prefer. (Java's.)
Barry Margolin <bar...@bbnplanet.com> writes: > In article <x2ww9xhbvv.fsf...@gairsay.aiai.ed.ac.uk>, > Jeff Dalton <j...@gairsay.aiai.ed.ac.uk> wrote: > >I'd like to see us define an interface for this functionality > >and then try to implement it in every Common Lisp in which it's > >possible to do so.
> >In this, it's important not to let the best be the enemy of the good. > >We should not let our desire to do things right stop us from doing > >something useful.
> Are you saying that this should be independent of the MOP, which already > provides this interface?
Well, there are some simple, introspective query operations that the MOP provides that can be thought of as a layer in themselves and separate from the more powerful and more controversial customizability stuff. I'm happy to have Jeff's suggestion turned into "let's find a few key functions in the MOP and standardize them ASAP". But if that can't be done, separate functions would be fine, too.
On Wed, 1 Jul 1998 13:22:00 GMT, Kent M Pitman <pit...@world.std.com> wrote:
> [My paraphrase: I still don't think Dylan "=" is useful.]
All I'm saying is that if you have several kinds of equality applicable to your new user defined type then it is handy to have an agreed place to go ("=") and get the natural kind of equality that you are likely to use most in your program.
Your "genericity" objection is that "=" is then a collection of different natural equalities, depending on the type. Surely the same arguments could be applied to instance manufacture, where some uses to MAKE-INSTANCE (or MAKE in Dylan) are a lot different from others: e.g. sometimes a whole deep tree of stuff is made, initialized, and connected to external resources. Other times, MAKE-INSTANCE is acting as little more than a shallow allocator.
Your "interaction" objection is that well-defined "=" methods in separate components may interact poorly when brought together. Perhaps you could give an example? In any case, in what way does this not also apply to instance manufacture?
But perhaps you think all such general protocols are ill-considered?
> Well, there are some simple, introspective query operations that the > MOP provides that can be thought of as a layer in themselves and separate > from the more powerful and more controversial customizability stuff. > I'm happy to have Jeff's suggestion turned into "let's find a few key > functions in the MOP and standardize them ASAP". But if that can't be > done, separate functions would be fine, too.
I think that Jeff's slot idea is really good, if it could be done via a MOPpy interface that would be better. I kind of had something that would do bits of it at some point, so that's at least some evidence that it is a real need!
Changing the subject *again*:
I would also really like to see enough MOP standardised to provide a functional equivalent of all the documented macros in CLOS. It seems to me that this should be pretty uncontentious and would make quite a lot of things easier to do. In particular not knowing what things like DEFCLASS expand into is a pain because it makes dynamic class creation hard. For instance I have a function called MIX-CLASSES which mixes a set of classes, or finds a premixed class which mixes them. Doing this needs something like ENSURE-CLASS, which `must' exist but is not documented (and varies between implementations).
Perhaps there is an issue with not allowing class (re)definition `at runtime' enabling some kind of sealing to go on, but there's already such a huge mass of support for redefinition in there that this seems not to be a good argument to me -- and in any case if you want sealing surely the right approach would be to provide some option which says that a class may not be redefined/subclassed/whatever.
It may be that ENSURE-CLASS is the only missing thing, I'm not sure off the top of my head. There does seem to be a slightly ragged edge between the stuff that got into ANSI (ALLOCATE-INSTANCE) and stuff that didn't (ENSURE-CLASS), which could usefully be cleaned up without going the whole MOP route (and unless there's a better suggestion than the one in AMOP I'd not like to see a complete MOP standardised yet).
* Jeff Dalton wrote: > You're right, but what should we therefore do? Java has a generic > "equals" and Common Lisp doesn't, and I know which answer I prefer. > (Java's.)
Is it not enough to provide (some version of) your slot-walking facility, in terms of which most (all?) of the interesting kinds of generic-equal can be defined anyway? Since the slot-walking stuff is interesting in any case this would be a good thing to do.
I've always found that the definition of equality is so widely variable that it's just more useful to write special-purpose predicates (which the slot-walking thing would allow to be much easier) than try and overload some function in too many ways. For instance in some code which used (red-black) trees I wanted to be able to ask at least:
are these two trees identical (EQ)? are these two trees structurally similar (need a slot walker)? are these two trees similar in the sense that they contain the same leaves in the same order (need a slot walker, and this is a different question to the previous one)?
So I need at least three predicates, two of which I need to be able to overload myself. In fact I think I had BT-EQUAL, BT-EQUIV.