Making == symmetric?

It has always bothered me that == is not symmetric in ruby:
a == b is shorthand for a.==(b), while b == a is shorthand for
b.==(a), and a and b might not agree on whether they are equal.

I have an idea as to how to fix this, and was thinking of posting an
RCR on rubygarden, but I thought I’d post here first and see if I’ve
missed any major problems.

First of all: yes, this issue does come up in real code.
Here’s a quick example I ran into today:

require “delegate”

class Foo
end

class D < SimpleDelegator
def initialize(obj)
super(obj)
end
end

f = Foo.new
d = D.new(f)

d == f #evaluates to true
f == d #evaluates to false

I ran into a similar problem trying to implement perl6-style
junctions: you end up with any(1,2,3) == 1 being true, but 1 ==
any(1,2,3) being false.

My proposed solution is this:
Instead of a == b being shorthand for a.==(b), it should be shorthand
for Kernel::compare(a, b).

def compare(a,b)
if a.can_compare_to?(b)
return a.compare_to(b)
elsif b.can_compare_to?(a)
return b.compare_to(a)
else
return false
end
end

This way, a class is only responsible for comparing itself to other
classes it knows about. This allows a lot more flexibility, because
you can define new classes that introduce their own semantics for
equality w.r.t existing classes (i.e., you can make a new class that
is equal to some strings, without having to change String to know
about the new class).

Compare this to the current situation, where an object is responsible
for comparing itself to any kind of object. In this situation, the
only sensible response for a class that you know nothing about is
"false", which limits flexibility, and can lead to == being
asymmetric.

Of course, this solution isn’t perfect – if a and b both know about
each other, but have diffening ideas of equality, then a == b and b ==
a will still return different results. But that’s a lot easier to
avoid than the problems which arise under the current handling of ==.

This solution might be extended to other operators that should be
symmetric, as well (such as +), but == is the most widely used and I
think the most important to fix.

For most binary operators, #coerce would seem to work (so long as the binary
operator exists in both classes), but not for ==. Perhaps that would be the
way to handle this?

-austin

···

On Wed, 1 Oct 2003 08:19:27 +0900, Nathan Weston wrote:

It has always bothered me that == is not symmetric in ruby: a == b is
shorthand for a.==(b), while b == a is shorthand for b.==(a), and a and b
might not agree on whether they are equal.

I have an idea as to how to fix this, and was thinking of posting an RCR
on rubygarden, but I thought I’d post here first and see if I’ve missed
any major problems.

First of all: yes, this issue does come up in real code. Here’s a quick
example I ran into today:

require “delegate”

class Foo
end

class D < SimpleDelegator
def initialize(obj)
super(obj)
end
end

f = Foo.new
d = D.new(f)

d == f #evaluates to true
f == d #evaluates to false

I ran into a similar problem trying to implement perl6-style junctions:
you end up with any(1,2,3) == 1 being true, but 1 == any(1,2,3) being
false.

My proposed solution is this:
Instead of a == b being shorthand for a.==(b), it should be shorthand for
Kernel::compare(a, b).

def compare(a,b)
if a.can_compare_to?(b)
return a.compare_to(b)
elsif b.can_compare_to?(a)
return b.compare_to(a)
else
return false
end
end

This way, a class is only responsible for comparing itself to other
classes it knows about. This allows a lot more flexibility, because you
can define new classes that introduce their own semantics for equality
w.r.t existing classes (i.e., you can make a new class that is equal to
some strings, without having to change String to know about the new
class).

Compare this to the current situation, where an object is responsible for
comparing itself to any kind of object. In this situation, the only
sensible response for a class that you know nothing about is “false”,
which limits flexibility, and can lead to == being asymmetric.

Of course, this solution isn’t perfect – if a and b both know about each
other, but have differing ideas of equality, then a == b and b == a will
still return different results. But that’s a lot easier to avoid than the
problems which arise under the current handling of ==.

This solution might be extended to other operators that should be
symmetric, as well (such as +), but == is the most widely used and I
think the most important to fix.


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.09.30
* 19.47.10

Those more knowledgeable than me should correct what I’ve written below
if it’s wrong.

It has always bothered me that == is not symmetric in ruby:
a == b is shorthand for a.==(b), while b == a is shorthand for
b.==(a), and a and b might not agree on whether they are equal.

I think it is symmetric so long as you bear in mind that the method
belongs to the object and not to the variable that refers to the
object. See below.

[snip]

First of all: yes, this issue does come up in real code.
Here’s a quick example I ran into today:

require “delegate”
class Foo
end
class D < SimpleDelegator
def initialize(obj)
super(obj)
end
end
f = Foo.new
d = D.new(f)
d == f #evaluates to true
f == d #evaluates to false

I think you’re mixing up variables and values. d refers to a D object
instantiated with a Foo object. f refers to the Foo object used to
instantiate the D object. The D object has an == method that checks its
value and compares it to the given value. When invoked by d==f, the D
object sees that its value is the Foo object and sees that f refers to
the Foo object and determines that they are the same object. In other
words the D object is the functional equivalent of an assignment to a
primitive variable.

The Foo object does not have a value other than its object id (I
think). When it queries the value of the variable d in performing the
== method, it gets the object id of the D object (I think). The two are
not equal. The proper comparison would be:

f.inspect==d.inspect

Another way of looking at it:

D.new(Foo.new) == Foo.new # false
Foo.new == D.new(Foo.new) # false

I ran into a similar problem trying to implement perl6-style
junctions: you end up with any(1,2,3) == 1 being true, but 1 ==
any(1,2,3) being false.

I didn’t follow the above point. There was an earlier discussion on the
list about perl6-style junctions that might be helpful.

[snip]

Regards,

Mark

···

On Tuesday, September 30, 2003, at 07:19 PM, Nathan Weston wrote:

Nathan Weston wrote:

def compare(a,b)
if a.can_compare_to?(b)
return a.compare_to(b)
elsif b.can_compare_to?(a)
return b.compare_to(a)
else
return false
end
end

How about

def compare(a, b)
retval = false
if a.can_compare_to(b)
retval = a.compare_to(b)
end
if retval and b.can_compare_to(a)
retval = b.compare_to(a)
end

return retval
end

This way if both of them know how to compare themselves to the other one,
the comparison returns true only if both agree they’re equal.

Aside from the code though, I’m not sure if I like the idea. While in
principle I agree that equality tests should be symmetrical, I also think
that it is more natural to have an equality operator which you can
overload. I think you’d still need “can_compare_to?” to know if ‘a’ knows
how to compare itself to an instance of ‘b’…

Maybe instead:

def compare(a, b)
retval = false
if a.can_compare_to?(b)
retval = a.==(b)
end
if retval and b.can_compare_to?(a)
retval = b.==(a)
end

return retval
end

Interesting food for thought though.

Ben

Nathan Weston wrote:

It has always bothered me that == is not symmetric in ruby:
a == b is shorthand for a.==(b), while b == a is shorthand for
b.==(a), and a and b might not agree on whether they are equal.

My own opinion on this is: == is like asking one object if it’s equal to
another. I know that particular operator is supposed to provide a
balanced equality test, but I only thought of == that way early in my
programming career; that notion has long since been replaced with the
notion that one side is asking if the other side is equal, and switching
things around will yield different results. Because of this, the way
Ruby handles == always made perfect sense to me.

But it would be nice to have a way to perform a perfectly symmetrical
balanced test.

Perhaps a solution would be to create a module that overrides == and
adds ==? to some or all classes. The ==? operator can return true,
false or nil. Nil would mean the method isn’t really an authority. The
overridden == method can try the ==? method for both objects. If one or
both ==? methods return true or false, that’s the answer. If both
return different, non-nil answers, an exception can be thrown. If both
return nil, then the super == method can be called to alert you that
both objects consider themselves an authority, but they are not
returning the same result.

Example:

class Object

def ==?(comparator)
return nil
end

def ==(comparate)
left = ==?(comparate)
right = comparate.==?(self)
if (left == nil and right == nil) then
return super
elsif (left == right)
return left and right ? true : false
elseif (left == nil)
return right
elseif (right == nil)
return left
else
raise “both comparates return unbalanced equality tests”
end

end

class String

def ==?(comparate)
return self.to_s == comparate.to_s if (comparate.class == Number)
return super
end

end

class Number

def ==?(comparate)
return self.to_s == comparate if (comparate.class == String)
return super
end

end

Clearly this is something that ought to be written in C, not Ruby, for
speed purposes, but the idea seems right to me. Everything can even be
kept in one module, expressly for the purpose of include’ing when
needed, so this sort of implementation wouldn’t break existing code.

Sean O'Dell

“Ben Giddings” bg-rubytalk@infofiend.com schrieb im Newsbeitrag
news:3F7A4EBC.20007@infofiend.com

Nathan Weston wrote:

def compare(a,b)
if a.can_compare_to?(b)
return a.compare_to(b)
elsif b.can_compare_to?(a)
return b.compare_to(a)
else
return false
end
end

How about

def compare(a, b)
retval = false
if a.can_compare_to(b)
retval = a.compare_to(b)
end
if retval and b.can_compare_to(a)
retval = b.compare_to(a)
end

return retval
end

This way if both of them know how to compare themselves to the other
one,
the comparison returns true only if both agree they’re equal.

But that’s inefficient to a degree that can cause problems for
applications. You end up always doing two comparisons. Even making the
compare() method more complex (i.e do only one comparison if the
instances’ classes match) doesn’t really solve this issue since for the
standard case you still get the overhead.

Aside from the code though, I’m not sure if I like the idea. While in
principle I agree that equality tests should be symmetrical, I also
think
that it is more natural to have an equality operator which you can
overload. I think you’d still need “can_compare_to?” to know if ‘a’
knows
how to compare itself to an instance of ‘b’…

Maybe instead:

def compare(a, b)
retval = false
if a.can_compare_to?(b)
retval = a.==(b)
end
if retval and b.can_compare_to?(a)
retval = b.==(a)
end

return retval
end

Doesn’t solve the overhead problem.

What one would really need is multiple dispatch:

module Kernel
def self.register_compare(classA, classB, &comp)
( @comparisons ||= {})[ [classA, classB] ] = comp
@comparisons[ [classB, classA] ] = proc { |b,a| comp.call(a, b) }
end

def self.compare(a, b)
c = @comparisons[ [a.class, b.class] ]
c.nil? ? a == b : c.call( a, b )
end
end

class Foo
Kernel.register_compare( self, self ) do |a,b|
puts “Foo == Foo”
false
end

def ==(b)
Kernel.compare( self, b )
end
end

class Bar < Foo
Kernel.register_compare( self, self ) do |a,b|
puts “Bar == Bar”
false
end

Kernel.register_compare( self, Foo ) do |a,b|
puts “Bar == Foo”
false
end
end

f1 = Foo.new
f2 = Foo.new

f1 == f2

b1 = Bar.new
b2 = Bar.new

b1 == b2

b1 == f1
f1 == b1

But this has several disadvantages, too: You keep creating all those
temporary arrays and you have the need for O(n**2) comparison operators.

Regards

robert

Mark Wilson mwilson13@cox.net wrote in message news:3AA52722-F3B5-11D7-951C-000393876156@cox.net

Those more knowledgeable than me should correct what I’ve written below
if it’s wrong.

It has always bothered me that == is not symmetric in ruby:
a == b is shorthand for a.==(b), while b == a is shorthand for
b.==(a), and a and b might not agree on whether they are equal.

I think it is symmetric so long as you bear in mind that the method
belongs to the object and not to the variable that refers to the
object. See below.

If == is symmetric, then (a == b) if and only if (b == a). In ruby,
this is not always the case, which can be very confusing.
I’m not sure what this has to do with the method belonging to the
object rather than the variable.

[snip]

[snip]

I think you’re mixing up variables and values. d refers to a D object
instantiated with a Foo object. f refers to the Foo object used to
instantiate the D object. The D object has an == method that checks its
value and compares it to the given value. When invoked by d==f, the D
object sees that its value is the Foo object and sees that f refers to
the Foo object and determines that they are the same object. In other
words the D object is the functional equivalent of an assignment to a
primitive variable.

[snip]

I understand how and why things work the way they do in ruby – I just
happen to think that way is wrong.

The issue in the above example is not whether f and d are equal (in
the code I am working on now, it would be convenient if they were, but
that’s beside the point). The issue is that f and d (or rather, the
objects they refer to), don’t agree on whether they are equal. I think
that is a bad thing.

Essentially what I am looking for is a way for an object, when asked
if it is equal to some other object, to answer “I don’t know”.

I ran into a similar problem trying to implement perl6-style
junctions: you end up with any(1,2,3) == 1 being true, but 1 ==
any(1,2,3) being false.

I didn’t follow the above point. There was an earlier discussion on the
list about perl6-style junctions that might be helpful.

Admittedly I didn’t elaborate on that point too much. I went back and
read the earlier discussion (which I actually started), and here is
the exact problem:

Suppose we have a method any(*args), which creates a junction object
of its arguments.

Then any(“1”, “2”, “3”) == “1” evaluates to true (which is correct).
However, “1” == any(“1”, “2”, “3”) evaluates to false (which is not
correct).

The problem is that String doesn’t know about the Junction class, and
therefore assumes it is not equal to any instances of it. (Maybe it
looks for a to_str method first, or somesuch – I don’t know the
implementation details exactly). There is no way around this, no
matter how you implement Junction, because in the second case the
comparison is don exclusively by String – none of Junction’s methods
are ever called.

Under my solution, a comparison between a “1” and any(“1”, “2”, “3”),
would first ask “1” if it knows how to compare itself to the junction.
The answer would be “no”, since the string class doesn’t know anything
about junctions. However, the junction does know how to compare itself
to a string, so the junction’s comparison method would be called
(regardless of which side of the == the junction is on).

Nathan

···

On Tuesday, September 30, 2003, at 07:19 PM, Nathan Weston wrote:

Austin Ziegler austin@halostatue.ca wrote in message news:2003930195425.321793@PADD

[snip]

For most binary operators, #coerce would seem to work (so long as the binary
operator exists in both classes), but not for ==. Perhaps that would be the
way to handle this?

-austin

#coerce only seems to work for Numerics (unless that has changed in
1.8?), and as you note, isn’t called for ==.
Actually, I think an extension of the coerce mechanism might be
cleaner than my proposal, and could avoid the potential problem (noted
in another post) of having to do 2 comparisons for each == operator.

I will think about it and post another proposal based on #coerce later
today.

Nathan

···

On Wed, 1 Oct 2003 08:19:27 +0900, Nathan Weston wrote:

Ben Giddings bg-rubytalk@infofiend.com wrote in message news:3F7A4EBC.20007@infofiend.com

Maybe instead:

def compare(a, b)
retval = false
if a.can_compare_to?(b)
retval = a.==(b)
end
if retval and b.can_compare_to?(a)
retval = b.==(a)
end

return retval
end

I agree that the use of == instead of compare_to is more intuitive if
people want to override equality. In general, I’m not attached to the
specific names I’ve thrown out in this example – Kernel#compare could
just as easily be Kernel#==, if that makes more sense.

Nathan

W liście z śro, 01-10-2003, godz. 19:31, Sean O’Dell pisze:

My own opinion on this is: == is like asking one object if it’s equal to
another. I know that particular operator is supposed to provide a
balanced equality test, but I only thought of == that way early in my
programming career; that notion has long since been replaced with the
notion that one side is asking if the other side is equal, and switching
things around will yield different results. Because of this, the way
Ruby handles == always made perfect sense to me.

This is like saying “my programming model is unable to describe the
reality accurately, so let’s change the reality to make it fit the
model”.

For me this is simply a shortcoming in the traditional single-dispatched
OO way of thinking, that it can’t make the meaning of == depend on the
types of both arguments.

I don’t say that the language should enforce == to be symmetric. I say
that the language should allow to define a symmetric == with the desired
meaning.

So I’m a fan of generic functions. Unfortunately they don’t quite fit
Ruby (or Ruby doesn’t quite fit my programming model) - I don’t know if
emulating them in Ruby is worth the effort.

···


__("< Marcin Kowalczyk
__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

“Robert Klemme” bob.news@gmx.net wrote in message news:ble00n$b7qp2$2@ID-52924.news.uni-berlin.de

“Ben Giddings” bg-rubytalk@infofiend.com schrieb im Newsbeitrag
news:3F7A4EBC.20007@infofiend.com

Nathan Weston wrote:

[snip]

Maybe instead:

def compare(a, b)
retval = false
if a.can_compare_to?(b)
retval = a.==(b)
end
if retval and b.can_compare_to?(a)
retval = b.==(a)
end

return retval
end

Doesn’t solve the overhead problem.

What one would really need is multiple dispatch:

module Kernel
def self.register_compare(classA, classB, &comp)
( @comparisons ||= {})[ [classA, classB] ] = comp
@comparisons[ [classB, classA] ] = proc { |b,a| comp.call(a, b) }
end

def self.compare(a, b)
c = @comparisons[ [a.class, b.class] ]
c.nil? ? a == b : c.call( a, b )
end
end

class Foo
Kernel.register_compare( self, self ) do |a,b|
puts “Foo == Foo”
false
end

def ==(b)
Kernel.compare( self, b )
end
end

class Bar < Foo
Kernel.register_compare( self, self ) do |a,b|
puts “Bar == Bar”
false
end

Kernel.register_compare( self, Foo ) do |a,b|
puts “Bar == Foo”
false
end
end

f1 = Foo.new
f2 = Foo.new

f1 == f2

b1 = Bar.new
b2 = Bar.new

b1 == b2

b1 == f1
f1 == b1

But this has several disadvantages, too: You keep creating all those
temporary arrays and you have the need for O(n**2) comparison operators.

Regards

robert

That seems a bit too messy to me. Just overriding == is cleaner and
more intuitive.
How about:

def compare(a, b)
if a.can_compare_to?(b)
return a.==(b)
elsif b.can_compare_to?(a)
return b.==(a)
end
return false
end

I think the problem Ben’s proposal is trying to avoid is something
like this:

class Foo
def can_compare_to?(obj)
if obj.is_a?(Bar)
return true
else

end

#Foo thinks that it is not equal to any Bar object
def ==(obj)
if obj.is_a?(Bar)
return false
else

end
end

class Bar
def can_compare_to?(obj)
if obj.is_a?(Foo)
return true
else

end

#Bar thinks that it is equal to any Foo object
def ==(obj)
if obj.is_a?(Foo)
return true
else

end
end

Under Ben’s solution:
Foo.new == Bar.new #false
Bar.new == Foo.new #false

So, the writer of Foo is happy, because he always gets the answer he
wanted. But the writer of Bar is unhappy, because he wanted true in
both cases.

Under my proposal
Foo.new == Bar.new #false
Bar.new == Foo.new #true

The writers of Foo and Bar are both unhappy, because they get the
wrong result in one case.

However, I would argue that, under both Ben’s proposal and my
proposal, you get broken behavior – it’s just broken in different
ways. Either way, to get non-broken behavior, you need the writers of
Foo and Bar to come to some agreement about how their classes are
compared (which I don’t think is unreasonable). And my way only does
one comparison, instead of 2.

Also, I suggest this default implentation of can_compare_to?

class Object
def can_compare_to?(obj)
return obj.kind_of?(self.class)
end
end

This way, in the common case where objects of class A are only equal
to other objects of class A, there is no need to override
can_compare_to?

The main problem I still see is the need to override can_compare_to?
at all. If this change is implemented, I expect many people will
override #== and be confused when this has no effect.

Maybe there is a way for #== to return an “I don’t know” result, which
could remove the need for can_compare_to? at all. Newbies could still
break things by writing == methods that return false as a default
(instead of “I don’t know”), but this would really only be a problem
if they were writing class libraries for someone else’s consumption
(which hopefully they wouldn’t be doing).

Nathan

First off, thank you for some fascinating ideas and questions. I happen
to like Sean O’Dell’s views further down in this thread.

I don’t know enough to have firm views on the topic, but I do have a
couple of thoughts.

  1. Sometimes, one will want two objects of the same class and with the
    same attributes to return false when the == operator is applied. For
    example, a pool of identical server objects should not be considered
    equal to each other.

  2. In general, two objects of different classes should return false
    when the == operator is applied to either of them, unless the classes
    differ only in name. I think whether this behavior is varied in
    specific cases would depend on how the class is meant to be used.

I would definitely like to hear more on this topic from others.

Regards,

Mark

···

On Wednesday, October 1, 2003, at 12:21 PM, Nathan Weston wrote:

[snip]

I think you’re mixing up variables and values. d refers to a D object
instantiated with a Foo object. f refers to the Foo object used to
instantiate the D object. The D object has an == method that checks
its
value and compares it to the given value. When invoked by d==f, the D
object sees that its value is the Foo object and sees that f refers to
the Foo object and determines that they are the same object. In other
words the D object is the functional equivalent of an assignment to a
primitive variable.

[snip]

I understand how and why things work the way they do in ruby – I just
happen to think that way is wrong.

The issue in the above example is not whether f and d are equal (in
the code I am working on now, it would be convenient if they were, but
that’s beside the point). The issue is that f and d (or rather, the
objects they refer to), don’t agree on whether they are equal. I think
that is a bad thing.

Essentially what I am looking for is a way for an object, when asked
if it is equal to some other object, to answer “I don’t know”.

[snip]

[snip]

This is like saying “my programming model is unable to describe the
reality accurately, so let’s change the reality to make it fit the
model”.

For me this is simply a shortcoming in the traditional
single-dispatched
OO way of thinking, that it can’t make the meaning of == depend on the
types of both arguments.

I don’t say that the language should enforce == to be symmetric. I say
that the language should allow to define a symmetric == with the
desired
meaning.

[snip]

I think Ruby does allow one to define a symmetric ==. As a general
matter, I think a language is only required to enforce a symmetric ==
for objects of the same class (or values of the same type) in order to
have algebraic closure.

Regards,

Mark

···

On Wednesday, October 1, 2003, at 02:16 PM, Marcin ‘Qrczak’ Kowalczyk wrote:

Moin!

Nathan Weston wrote:

trying to implement perl6-style junctions
Suppose we have a method any(*args), which creates a junction object
of its arguments.

Then any(“1”, “2”, “3”) == “1” evaluates to true (which is correct).
However, “1” == any(“1”, “2”, “3”) evaluates to false (which is not
correct).

The problem is that String doesn’t know about the Junction class, and
therefore assumes it is not equal to any instances of it. (Maybe it
looks for a to_str method first, or somesuch – I don’t know the
implementation details exactly). There is no way around this, no
matter how you implement Junction, because in the second case the
comparison is don exclusively by String – none of Junction’s methods
are ever called.

Junctions are interesting things to implement. (Even if it’s
questionable whether they really make sense in Ruby when doing something
else than passing multiple objects to a method which only expects a
single one…)

I’ve actually implemented them already in a few lines of plain Ruby
code: http://rubyforge.org/projects/junction/

I solved the problem of built-in methods not knowing what to do with
Junctions by plainly hiding them behind proxies – this makes
interesting things like “food taste good”.match(%w{foo bar qux}.any)
possible.

The problem with this approach is that it’s quite ugly to implement and
that it might even cause some bad side-effects. (Because of this I
didn’t include this in the actual Junction implementation itself.)

Oh, and to really make Junctions perl compatible you would need to
replace any method call including Junctions as parameters with a
Junction constructed of the results of the method call with all the
Junction’s states anyway. (Perl6 doesn’t pass Junctions into methods by
default because it fears that Junction logic might be confusing. (For
example in this code you could get both “foo!” and “bar!” if arg is a
Junction: puts “foo!” if arg == 1; puts “bar!” if arg == 2))

Regards,
Florian Groß

Marcin ‘Qrczak’ Kowalczyk wrote:

W li¶cie z ¶ro, 01-10-2003, godz. 19:31, Sean O’Dell pisze:

My own opinion on this is: == is like asking one object if it’s equal to
another. I know that particular operator is supposed to provide a
balanced equality test, but I only thought of == that way early in my
programming career; that notion has long since been replaced with the
notion that one side is asking if the other side is equal, and switching
things around will yield different results. Because of this, the way
Ruby handles == always made perfect sense to me.

This is like saying “my programming model is unable to describe the
reality accurately, so let’s change the reality to make it fit the
model”.

The notion of a symmetrical equality test is an abstract concept. In
reality, whether it’s Ruby or anything else, testing is NOT symmetrical.

If you compare two objects in real life, you have to compare one to the
other by first observing one, then the second object. You can decide to
either observe in one pass (object A first, then object B) or can
observe a second time (object B first, then object A) and decide if they
are equal.

You can make as many passes as you want, or you can just make one pass.

If you say:

if (a == b) then

… then you are accepting one pass. If you say:

if (a == b and b == a) then

… then you are demanding a second pass and trying to get a better idea
if they are equal or not.

Although I want to know if A and B are equal, I accept that the equality
test is one object comparing itself to another. This is acceptable to
me, and if I want more, I can override the == method or perform more
tests to get the result I want.

For me this is simply a shortcoming in the traditional single-dispatched
OO way of thinking, that it can’t make the meaning of == depend on the
types of both arguments.

I don’t say that the language should enforce == to be symmetric. I say
that the language should allow to define a symmetric == with the desired
meaning.

But it does. You can very easily override == just as I described in my
example code. You can make == anything you want it to be, for any
class, existing or not.

So I’m a fan of generic functions. Unfortunately they don’t quite fit
Ruby (or Ruby doesn’t quite fit my programming model) - I don’t know if
emulating them in Ruby is worth the effort.

I’m just glad Ruby lets us override and emulate … to our heart’s
content, it lets us. =)

Sean O'Dell

I don’t even know if I’ve been using Ruby long enough( the past few days )
to even comment, so I’ll keep my opinions brief.

In regards to what Mark Wilson said about Sean O’Dells comments:

I don’t know enough to have firm views on the topic, but I do have a
couple of thoughts.

  1. Sometimes, one will want two objects of the same class and with the
    same attributes to return false when the == operator is applied. For
    example, a pool of identical server objects should not be considered
    equal to each other.

  2. In general, two objects of different classes should return false
    when the == operator is applied to either of them, unless the classes
    differ only in name. I think whether this behavior is varied in
    specific cases would depend on how the class is meant to be used.

on #1. I agree with Mark and I’d also like to say…why would you want
to use the == to return true when comparing different objects, even if
they are of the same class, with the same attributes. Unless one’s a pointer
or reference and you are using an equivalency of a === test. I don’t see why
someone would want this expected behavior from == itself.

on #2. I think 2 different classes should always return false when compared
to
one another. Otherwise you are getting away from the simplicity and purpose
of a comparison in it’s most generic form and you are bringing
in special cases where “behavior is varied”. If you want to allow for
special case scenario’s then perhaps make a new comparison operator/method
and leave == alone.

I way even be off with my whole post, if I am just tell me to hush up. =)

-Zach

W liście z śro, 01-10-2003, godz. 20:34, Mark Wilson pisze:

I think Ruby does allow one to define a symmetric ==.

Only if you agree to limit cases of “different class but equal” to those
where both classes know each other. Because otherwise you would have to
change the implementation of == in other classes which were already
written elsewhere.

For example there doesn’t seem to be a way to add a numeric type which
plays well with other numeric types wrt. ==, including numeric types you
don’t know about.

As a general
matter, I think a language is only required to enforce a symmetric ==
for objects of the same class (or values of the same type) in order to
have algebraic closure.

I prefer to trust the programmer in this case. The only sensible way I
imagine in which the language could enforce a symmetric == is to execute
the programmer-provided equality in both directions and throw error if
they disagree, and this is too little benefit (catching rare bugs) for
too much cost (performance of a common operation). Turning the
disagreement into false or true would mask an error, so it only
discovers bugs - it doesn’t add functionality.

···


__("< Marcin Kowalczyk
__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

W liście z śro, 01-10-2003, godz. 21:42, Sean O’Dell pisze:

The notion of a symmetrical equality test is an abstract concept. In
reality, whether it’s Ruby or anything else, testing is NOT symmetrical.

The method used to determine the outcome is not symmetrical internally,
but the outcome is (or rather: should be).

If you say:

if (a == b) then

… then you are accepting one pass. If you say:

if (a == b and b == a) then

… then you are demanding a second pass and trying to get a better idea
if they are equal or not.

My concept of == requires that they give the same result, i.e. if they
differ then some definition of == must be insane (probably a programmer-
-supplied interpretation of == for a particular type).

I mean that if an algorithm relies on == being symmetrical but doesn’t
explicitly check both directions and fails because of this, then the
fault is at the definition of ==, not in the algorithm which uses ==.

It wouldn’t know what to do with conflicting answers anyway, except to
signal error. Why do you use “and” and not “or” for example?

Well, another, equally valid requirement is x==x, which is usually not
satisied by NaN. I say: it is indeed against the rules; sometimes it’s
useful to make an exception. You shouldn’t rely much on equality of
floats anyway.

I don’t say that the language should enforce == to be symmetric. I say
that the language should allow to define a symmetric == with the desired
meaning.

But it does. You can very easily override == just as I described in my
example code. You can make == anything you want it to be, for any
class, existing or not.

How to make a new numeric type (say, long integer objects from another
language or library wrapped as Ruby objects) and make == between this
type and other numeric types compute the equality of numbers being
represented? You don’t know all other numeric types.

···


__("< Marcin Kowalczyk
__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Zach Dennis wrote:

on #1. I agree with Mark and I’d also like to say…why would you want
to use the == to return true when comparing different objects, even if
they are of the same class, with the same attributes. Unless one’s a pointer
or reference and you are using an equivalency of a === test. I don’t see why
someone would want this expected behavior from == itself.

Well, for example:

irb(main):001:0> [1,2] == [1,2]
=> true

Also, strings.

Of course, this isn’t really a “class with attributes”, but even for
user defined classes, I sometimes define #== to work this way. This
seems like the right thing to do for geometrical objects, like points. YMMV.

on #2. I think 2 different classes should always return false when compared
to
one another. Otherwise you are getting away from the simplicity and purpose
of a comparison in it’s most generic form and you are bringing
in special cases where “behavior is varied”. If you want to allow for
special case scenario’s then perhaps make a new comparison operator/method
and leave == alone.

irb(main):002:0> 1.0 == 1
=> true

Or, if you had two Point classes, one maintaining its state in polar
coordinates, and the other in cartesian coordinates, you might want
equality to transcend class boundaries.

“Zach Dennis” zdennis@mktec.com wrote in message news:AKEKIKLMCFIHPEAHKAAIMEFDGKAA.zdennis@mktec.com

I don’t even know if I’ve been using Ruby long enough( the past few days )
to even comment, so I’ll keep my opinions brief.

In regards to what Mark Wilson said about Sean O’Dells comments:

I don’t know enough to have firm views on the topic, but I do have a
couple of thoughts.

  1. Sometimes, one will want two objects of the same class and with the
    same attributes to return false when the == operator is applied. For
    example, a pool of identical server objects should not be considered
    equal to each other.

  2. In general, two objects of different classes should return false
    when the == operator is applied to either of them, unless the classes
    differ only in name. I think whether this behavior is varied in
    specific cases would depend on how the class is meant to be used.

on #1. I agree with Mark and I’d also like to say…why would you want
to use the == to return true when comparing different objects, even if
they are of the same class, with the same attributes. Unless one’s a pointer
or reference and you are using an equivalency of a === test. I don’t see why
someone would want this expected behavior from == itself.

on #2. I think 2 different classes should always return false when compared
to
one another. Otherwise you are getting away from the simplicity and purpose
of a comparison in it’s most generic form and you are bringing
in special cases where “behavior is varied”. If you want to allow for
special case scenario’s then perhaps make a new comparison operator/method
and leave == alone.

If you just want to find out whether two references point to the same
object, that’s what the equal? method is for. It’s defined in Object
and explicitly not meant to be overriden.

==, on the other hand, is supposed to be used for richer comparisons,
and subclasses are free to override it. In fact, == is already
overridden by several classes in the standard libraries, like String
and Array.

Nathan