N
Nathan Weston
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.
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.