In the little application I am using, I have a handful of `defstruct'-ed
items sitting around that I play with.
One of the things I occasionally need to do is to determine if two of
these are equal -- that all their slots, etc, have the same value.
The structures in question are:
(defstruct ip-address
"Store an Internet Protocol V4 address."
(address nil :type (vector (integer 0 255) 4))
(netmask 32 :type (integer 0 32)))
Under GNU CLISP 2.27 (released 2001-07-17) these are:
(setq a (make-ip-address (coerce '(0 0 0 0) '(vector (integer 0 255) 4)) 0))
(setq b (make-ip-address (coerce '(0 0 0 0) '(vector (integer 0 255) 4)) 0))
(equal a b)
=> nil
(equalp a b)
=> t
This seems, so far as I can tell from the HyperSpec, to be the defined
behavior of `equal' and `equalp' when dealing with structures -- they
are `equal' iff they are `eq', but `equalp' if they have the same
content in their slots.
Reading through the notes on the two from when the standard was being
dealt with, I am not *totally* certain how portable this is, though.
Can I rely on "(equalp a b) => t" from above working identically under
any[1] Common Lisp?
Daniel
Footnotes:
[1] Any conforming implementation, anyway. :)
--
Real Programmers consider "what you see is what you get" to be just as bad a
concept in Text Editors as it is in women. No, the Real Programmer wants a
"you asked for it, you got it" text editor -- complicated, cryptic, powerful,
unforgiving, dangerous.
"Daniel Pittman" <······@rimspace.net> wrote in message
···················@inanna.rimspace.net...
> In the little application I am using, I have a handful of `defstruct'-ed
> items sitting around that I play with.
>
> One of the things I occasionally need to do is to determine if two of
> these are equal -- that all their slots, etc, have the same value.
>
> The structures in question are:
>
> (defstruct ip-address
> "Store an Internet Protocol V4 address."
> (address nil :type (vector (integer 0 255) 4))
> (netmask 32 :type (integer 0 32)))
>
> Under GNU CLISP 2.27 (released 2001-07-17) these are:
>
> (setq a (make-ip-address (coerce '(0 0 0 0) '(vector (integer 0 255) 4)) 0))
> (setq b (make-ip-address (coerce '(0 0 0 0) '(vector (integer 0 255) 4)) 0))
>
> (equal a b)
> => nil
> (equalp a b)
> => t
>
> This seems, so far as I can tell from the HyperSpec, to be the defined
> behavior of `equal' and `equalp' when dealing with structures -- they
> are `equal' iff they are `eq', but `equalp' if they have the same
> content in their slots.
>
> Reading through the notes on the two from when the standard was being
> dealt with, I am not *totally* certain how portable this is, though.
>
> Can I rely on "(equalp a b) => t" from above working identically under
> any[1] Common Lisp?
>
> Daniel
>
Not commenting on the HyperSpecific part of your question, but in general you
are best to write
--
(remove #\space "coby . beck @ opentechgroup . com") your own equality method
that understands what it is you really mean be "the same". If some of your
slots will contain other structures or classes equalp might not know what you
mean. I usually just define an equal? predicate for any class I will need it
for
(defmethod equal? ((obj1 foo) (obj2 foo))
(and (eq (slot1 obj1) (slot1 obj2))
(= (slot2 obj1) (slot2 obj2))
(equalp (slot3 obj1) (slot3 obj2))
(close-enough (slot4 obj1) (slot4 obj2))
(equal? (slot5 obj1) (slot5 obj2))))
something like that...
Coby
"Daniel Pittman" <······@rimspace.net> wrote in message
···················@inanna.rimspace.net...
> In the little application I am using, I have a handful of `defstruct'-ed
> items sitting around that I play with.
>
> One of the things I occasionally need to do is to determine if two of
> these are equal -- that all their slots, etc, have the same value.
>
> The structures in question are:
>
> (defstruct ip-address
> "Store an Internet Protocol V4 address."
> (address nil :type (vector (integer 0 255) 4))
> (netmask 32 :type (integer 0 32)))
>
> Under GNU CLISP 2.27 (released 2001-07-17) these are:
>
> (setq a (make-ip-address (coerce '(0 0 0 0) '(vector (integer 0 255) 4)) 0))
> (setq b (make-ip-address (coerce '(0 0 0 0) '(vector (integer 0 255) 4)) 0))
>
> (equal a b)
> => nil
> (equalp a b)
> => t
>
> This seems, so far as I can tell from the HyperSpec, to be the defined
> behavior of `equal' and `equalp' when dealing with structures -- they
> are `equal' iff they are `eq', but `equalp' if they have the same
> content in their slots.
>
> Reading through the notes on the two from when the standard was being
> dealt with, I am not *totally* certain how portable this is, though.
>
> Can I rely on "(equalp a b) => t" from above working identically under
> any[1] Common Lisp?
>
> Daniel
>
Not commenting on the HyperSpecific part of your question, but in general you
are best to write your own equality method that understands what it is you
really mean be "the same". If some of your slots will contain other structures
or classes equalp might not know what you mean. I usually just define an
equal? predicate for any class I will need it for
(defmethod equal? ((obj1 foo) (obj2 foo))
(and (eq (slot1 obj1) (slot1 obj2))
(= (slot2 obj1) (slot2 obj2))
(equalp (slot3 obj1) (slot3 obj2))
(close-enough (slot4 obj1) (slot4 obj2))
(equal? (slot5 obj1) (slot5 obj2))))
something like that...
Coby
--
(remove #\space "coby . beck @ opentechgroup . com")
From: Kent M Pitman
Subject: Re: Determining structure equivalence, not structure eq-ness.
Date:
Message-ID: <sfwzo6up54r.fsf@world.std.com>
"Coby Beck" <·····@mercury.bc.ca> writes:
> "Daniel Pittman" <······@rimspace.net> wrote in message
> ···················@inanna.rimspace.net...
> > In the little application I am using, I have a handful of `defstruct'-ed
> > items sitting around that I play with.
> >
> > One of the things I occasionally need to do is to determine if two of
> > these are equal -- that all their slots, etc, have the same value.
> >
> > The structures in question are:
> >
> > (defstruct ip-address
> > "Store an Internet Protocol V4 address."
> > (address nil :type (vector (integer 0 255) 4))
> > (netmask 32 :type (integer 0 32)))
> >
> > Under GNU CLISP 2.27 (released 2001-07-17) these are:
> >
> > (setq a (make-ip-address (coerce '(0 0 0 0) '(vector (integer 0 255) 4)) 0))
> > (setq b (make-ip-address (coerce '(0 0 0 0) '(vector (integer 0 255) 4)) 0))
> >
> > (equal a b)
> > => nil
> > (equalp a b)
> > => t
> >
> > This seems, so far as I can tell from the HyperSpec, to be the defined
> > behavior of `equal' and `equalp' when dealing with structures -- they
> > are `equal' iff they are `eq', but `equalp' if they have the same
> > content in their slots.
> >
> > Reading through the notes on the two from when the standard was being
> > dealt with, I am not *totally* certain how portable this is, though.
Portability is an issue of intent. It will portably do what the words say,
but there are different senses of that. Is RANDOM portable? GENSYM? The
words tell you what will happen, and whether what will happen is adequate
is a matter for you, the programmer to decide.
But you're right to worry that this might not be what you want. I doubt
it usually is what people want, frankly. I argued for not including EQUALP
in the language, but in fairness I also argued for not including EQUAL either.
Equality and copying are complex issues that people don't understand well,
and CL has indulged that lack of understanding by allowing very generic names
for functions that are not nearly as generic as they seem. To me, that was
a questionable decision even though I'm equally (heh) sure that people would
have hated me forever had I gotten my way and had EQUAL not been included.
As it is, everyone asks where COPY is and I always say it's just as hard
as EQUAL and they say "but you included EQUAL"... to which I have no answer
other than "committee decisions are strange things". See also my paper:
http://world.std.com/~pitman/PS/EQUAL.html
which treats some of this issue in considerably more detail.
> > Can I rely on "(equalp a b) => t" from above working identically under
> > any[1] Common Lisp?
> >
> > Daniel
> >
>
> Not commenting on the HyperSpecific part of your question, but in general you
> are best to write your own equality method that understands what it is you
> really mean be "the same". If some of your slots will contain other structures
> or classes equalp might not know what you mean. I usually just define an
> equal? predicate for any class I will need it for
>
> (defmethod equal? ((obj1 foo) (obj2 foo))
> (and (eq (slot1 obj1) (slot1 obj2))
> (= (slot2 obj1) (slot2 obj2))
> (equalp (slot3 obj1) (slot3 obj2))
> (close-enough (slot4 obj1) (slot4 obj2))
> (equal? (slot5 obj1) (slot5 obj2))))
>
> something like that...
What Coby suggests is a lot of work, but I really think he's got the right
idea. We picked EQUAL and EQUALP to just have "some statistically useful
properties" but we gave you little control over tuning it. Taking matters
into your own hands is not that hard.
Incidentally, the only way I'd disagree with Coby here is that I wouldn't
call it EQUAL? I'd give it a less generic name that tried to describe my
intent better. Otherwise, you're just replicating the same philosophical
error only "in the small" (local to your package); you're inviting someone
to see your EQUAL? function and use it for other purposes it won't hold up
to. Make it like "EQUAL-FOR-COOKING?" or "MONETARILY-EQUAL?" or
"AESTHETICALLY-EQUAL-ACCORDING-TO-MARY?" or something like that which leaves
room for someone to make their own equally (heh) useful equality function
in the same namespace without fighting you for the "good name".
From: Daniel Pittman
Subject: Re: Determining structure equivalence, not structure eq-ness.
Date:
Message-ID: <87g08l4h7e.fsf@inanna.rimspace.net>
On Sun, 14 Oct 2001, Kent M. Pitman wrote:
> "Coby Beck" <·····@mercury.bc.ca> writes:
>> "Daniel Pittman" <······@rimspace.net> wrote in message
>> ···················@inanna.rimspace.net...
>> > In the little application I am using, I have a handful of
>> > `defstruct'-ed items sitting around that I play with.
>> >
>> > One of the things I occasionally need to do is to determine if two
>> > of these are equal -- that all their slots, etc, have the same
>> > value.
[...]
>> > This seems, so far as I can tell from the HyperSpec, to be the
>> > defined behavior of `equal' and `equalp' when dealing with
>> > structures -- they are `equal' iff they are `eq', but `equalp' if
>> > they have the same content in their slots.
[...]
> Portability is an issue of intent. It will portably do what the words
> say, but there are different senses of that. Is RANDOM portable?
> GENSYM? The words tell you what will happen, and whether what will
> happen is adequate is a matter for you, the programmer to decide.
Maybe I wasn't clear. I follow this logic -- I just wasn't sure
*exactly* what the spec indicated was required of an implementor and
what wasn't.
> But you're right to worry that this might not be what you want. I
> doubt it usually is what people want, frankly. I argued for not
> including EQUALP in the language, but in fairness I also argued for
> not including EQUAL either.
I noticed that, actually. It's one of the reasons I asked here, as I
felt you would probably offer some clarification of your position. :)
> Equality and copying are complex issues that people don't understand
> well, and CL has indulged that lack of understanding by allowing very
> generic names for functions that are not nearly as generic as they
> seem.
I agree with the first part, but I am less convinced of the second.
I think that there *are* some generic "equality" tests and, if the
version of equal is well documented, it's not such a bad thing to
support them.
OTOH I do imagine that there are people out there who made the same
silly mistake as I did, wrote
(pushnew (make-some-struct) place :test #'equal)
and got a nasty surprise when it didn't work quite right.
> To me, that was a questionable decision even though I'm equally (heh)
> sure that people would have hated me forever had I gotten my way and
> had EQUAL not been included.
Almost certainly, if for no other reason but that it would have made it,
er, difficult to implement equality tests for simple POD types (FIXNUM,
char, et al) without the explosion of '(and (char-equal ..)
(integer-equal ..)' expressions in the code.
> As it is, everyone asks where COPY is and I always say it's just as
> hard as EQUAL and they say "but you included EQUAL"... to which I have
> no answer other than "committee decisions are strange things". See
> also my paper: http://world.std.com/~pitman/PS/EQUAL.html which treats
> some of this issue in considerably more detail.
Cool. Thanks for the reference; I will read through it when I get a
chance. I hope that I don't annoy you by putting it off a bit to keep
this discussion relatively fresh. :)
[...]
> What Coby suggests is a lot of work, but I really think he's got the
> right idea. We picked EQUAL and EQUALP to just have "some
> statistically useful properties" but we gave you little control over
> tuning it. Taking matters into your own hands is not that hard.
It's not, I suppose, but it's vaguely annoying for small programs where
the test I want is "looks the same" or "is the same" -- the `eq' and
`equal' tests for lists and such.
Both of those have good, well defined properties. I was very surprised,
actually, to find that `equal' for a struct was implemented in terms of
`eq'; that redefined the use of `equal' for me.
At least the HyperSpec discussed the *why* of it and the related issues
well so that I didn't just shake my head and assume that it was an
implementation bug -- after dealing with the Emacs lisp engine for a
while, I got used to the non-CL behavior.
> Incidentally, the only way I'd disagree with Coby here is that I
> wouldn't call it EQUAL? I'd give it a less generic name that tried to
> describe my intent better. Otherwise, you're just replicating the same
> philosophical error only "in the small" (local to your package);
If I were going to ditch `equal' and friends entirely, I would certainly
chose this path. In many, but not in all, software abstractions, there
is more than one possible definition of equality and it's best to
express which one you use.
Daniel
--
They only hit until you cry
And after that you don't ask why
You just don't argue anymore
-- Suzanne Vega, _Luka_ (Solitude)