This page is no longer maintained — Please continue to the home page at www.scala-lang.org

nobody ever tires of equality

10 replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.

Here's a whole separate category of perversion which comes up under
proposed 2.8 equality. It's nice that I'm still finding new ones, I'd
hate for it all to seem dreary and ho-hum.

scala> trait MySeq { val xs: Seq[Any] }
defined trait MySeq

scala> val s1 = new MySeq { val xs: Seq[Any] = Seq(1) }
s1: java.lang.Object with MySeq = $anon$1@2dcc5af0

scala> val s2 = new MySeq { val xs: Seq[Any] = Seq(1L) }
s2: java.lang.Object with MySeq = $anon$1@62b92dc2

// this is false because it is comparing (1: Any) and (1L: Any)
scala> (s1.xs zip s2.xs) forall Function.tupled(_ == _)
res1: Boolean = false

// now for the next two we will let the type of xs be inferred.
// remember that xs is declared as Seq[Any] and we never say
// otherwise!
scala> val s3 = new MySeq { val xs = Seq(1) }
s3: java.lang.Object with MySeq{def xs: Seq[Int]} = $anon$1@21c8dfe6

scala> val s4 = new MySeq { val xs = Seq(1L) }
s4: java.lang.Object with MySeq{def xs: Seq[Long]} = $anon$1@3eb217d5

// but since Seq is covariant, scala is happy to specialize the
// type beyond Seq[Any] if you give it all creatures of one type.
// And in this case that means....
scala> (s3.xs zip s4.xs) forall Function.tupled(_ == _)
res3: Boolean = true

...we're comparing (1: Int) and (1: Long) which is true.

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: nobody ever tires of equality

On Mon, Oct 26, 2009 at 10:28 PM, Paul Phillips wrote:
> Here's a whole separate category of perversion which comes up under
> proposed 2.8 equality.  It's nice that I'm still finding new ones, I'd
> hate for it all to seem dreary and ho-hum.
>
> scala> trait MySeq { val xs: Seq[Any] }
> defined trait MySeq
>
> scala> val s1 = new MySeq { val xs: Seq[Any] = Seq(1) }
> s1: java.lang.Object with MySeq = $anon$1@2dcc5af0
>
> scala> val s2 = new MySeq { val xs: Seq[Any] = Seq(1L) }
> s2: java.lang.Object with MySeq = $anon$1@62b92dc2
>
> // this is false because it is comparing (1: Any) and (1L: Any)
> scala> (s1.xs zip s2.xs) forall Function.tupled(_ == _)
> res1: Boolean = false
>
> // now for the next two we will let the type of xs be inferred.
> // remember that xs is declared as Seq[Any] and we never say
> // otherwise!
> scala> val s3 = new MySeq { val xs = Seq(1) }
> s3: java.lang.Object with MySeq{def xs: Seq[Int]} = $anon$1@21c8dfe6
>
> scala> val s4 = new MySeq { val xs = Seq(1L) }
> s4: java.lang.Object with MySeq{def xs: Seq[Long]} = $anon$1@3eb217d5
>
> // but since Seq is covariant, scala is happy to specialize the
> // type beyond Seq[Any] if you give it all creatures of one type.
> // And in this case that means....
> scala> (s3.xs zip s4.xs) forall Function.tupled(_ == _)
> res3: Boolean = true
>
> ...we're comparing (1: Int) and (1: Long) which is true.
>
I think all this just shows that static overloading resolution can be
subtle because you need to anticipate the result of type inference.
But none of that is new. Users deal with that usually in that they
stay away from situations that could be tricky. In our case, they
would probably be reluctant to mix elements of different primitive
types in one list or collection. That's just as well.

Faced with a tricky situation like this, I think it's important to
pick the simplest rules possible. If you do that, there might be
observations which are surprising at first, but users are able to work
out in the end why things worked the way they did. Unless it is 100%
leak-free, a more complicated scheme would hurt rather than help.

Cheers

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: nobody ever tires of equality

On Tue, Oct 27, 2009 at 09:39:57AM +0100, martin odersky wrote:
> Faced with a tricky situation like this, I think it's important to
> pick the simplest rules possible. If you do that, there might be
> observations which are surprising at first, but users are able to work
> out in the end why things worked the way they did. Unless it is 100%
> leak-free, a more complicated scheme would hurt rather than help.

This makes it sound like I am advocating a different and presumably more
complicated scheme, but I'm not, I endorse no plan. I'm sending all
these examples so nobody can say later they didn't know what they were
getting. Hey scala users, don't count on poor paulp to whip up a whole
new equality implementation for 2.8.1 if you don't get around to
thinking about this one until after it has shipped.

The fact that people have accepted all these examples pretty much in
silence bodes well, I think, assuming it's not only the apathy speaking.
I have a knack for seeing the dangerous side of everything a bit too
keenly, so if I'm passing out end-of-the-world fliers and no one seems
to be heading for their bunker, I can take a dandelion break. I'm sure
I'll be able to see any developing mushroom clouds from over there.

Matt Fowles
Joined: 2009-07-09,
User offline. Last seen 42 years 45 weeks ago.
Re: nobody ever tires of equality
Paul~

Not entirely silently.  I offered what I felt was a simpler solution and was told that it was not to happen.

I still feel that if we do anything more complicated the strict reference equality for '==', we will cause no end of annoying bugs, but I get the distinct impression that this ship has sailed.

Matt

On Tue, Oct 27, 2009 at 7:57 PM, Paul Phillips <paulp@improving.org> wrote:
On Tue, Oct 27, 2009 at 09:39:57AM +0100, martin odersky wrote:
> Faced with a tricky situation like this, I think it's important to
> pick the simplest rules possible. If you do that, there might be
> observations which are surprising at first, but users are able to work
> out in the end why things worked the way they did. Unless it is 100%
> leak-free, a more complicated scheme would hurt rather than help.

This makes it sound like I am advocating a different and presumably more
complicated scheme, but I'm not, I endorse no plan.  I'm sending all
these examples so nobody can say later they didn't know what they were
getting.  Hey scala users, don't count on poor paulp to whip up a whole
new equality implementation for 2.8.1 if you don't get around to
thinking about this one until after it has shipped.

The fact that people have accepted all these examples pretty much in
silence bodes well, I think, assuming it's not only the apathy speaking.
I have a knack for seeing the dangerous side of everything a bit too
keenly, so if I'm passing out end-of-the-world fliers and no one seems
to be heading for their bunker, I can take a dandelion break.  I'm sure
I'll be able to see any developing mushroom clouds from over there.

--
Paul Phillips      | Before a man speaks it is always safe to assume
Vivid              | that he is a fool.  After he speaks, it is seldom
Empiricist         | necessary to assume it.
pp: i haul pills   |     -- H. L. Mencken

Jesper Nordenberg
Joined: 2008-12-27,
User offline. Last seen 42 years 45 weeks ago.
Re: nobody ever tires of equality

I'm with Paul on this one. The proposed behavior will cause some serious confusion and hard-to-find bugs. Regarding alternative solutions, I'm still not convinced why the == behavior must be consistent with Object.hashCode() and not some other Scala hash code function. As for Java interoperability, Java code only uses equals() which of course should be consistent with hashCode(). Which equality/hash code function pair to use in Scala collection classes could be configurable using constructor parameters.

/Jesper Nordenberg

fanf
Joined: 2009-03-17,
User offline. Last seen 2 years 30 weeks ago.
Re: nobody ever tires of equality

On 28/10/2009 00:57, Paul Phillips wrote:
> The fact that people have accepted all these examples pretty much in
> silence bodes well, I think, assuming it's not only the apathy speaking.

All the "equality case" continues to frigthen me, and clearly all these
counter intuitive (and even not so clear when looked at) examples raised
by Paul don't make me feel better.

I do understand that there is compatibility issue with Java, but all
these corners cases defeat my love of "least surprise" principle... I'm
foreseeing strange bugs coming in the future, with hours of debugging,
just because somewhere, someone forgot a "L".

For now, I continue to think that I would really much more enjoy a world
where two different primitive types whould never be equals, and where
the compiler could help me here, warning that I "1 == 1L" is never true...

But well, I'm in no way a Type specialist, nor I spend hours and hours
trying to understand the problem and all its ramification. I just warn
that, as an user, I won't be happy when I will (because I will, or my
team, or new user, again and again) be hit by these behaviours.

--
Francois Armand

Roland Kuhn
Joined: 2008-12-26,
User offline. Last seen 3 years 14 weeks ago.
Re: nobody ever tires of equality

On Oct 27, 2009, at 09:39 , martin odersky wrote:

> On Mon, Oct 26, 2009 at 10:28 PM, Paul Phillips
> wrote:
>> Here's a whole separate category of perversion which comes up under
>> proposed 2.8 equality. It's nice that I'm still finding new ones,
>> I'd
>> hate for it all to seem dreary and ho-hum.
[snip]
>> // this is false because it is comparing (1: Any) and (1L: Any)
>> scala> (s1.xs zip s2.xs) forall Function.tupled(_ == _)
>> res1: Boolean = false
[snip]
>> scala> (s3.xs zip s4.xs) forall Function.tupled(_ == _)
>> res3: Boolean = true
>>
>> ...we're comparing (1: Int) and (1: Long) which is true.
>>
> I think all this just shows that static overloading resolution can be
> subtle because you need to anticipate the result of type inference.
> But none of that is new. Users deal with that usually in that they
> stay away from situations that could be tricky. In our case, they
> would probably be reluctant to mix elements of different primitive
> types in one list or collection. That's just as well.
>
> Faced with a tricky situation like this, I think it's important to
> pick the simplest rules possible. If you do that, there might be
> observations which are surprising at first, but users are able to work
> out in the end why things worked the way they did. Unless it is 100%
> leak-free, a more complicated scheme would hurt rather than help.
>
The scheme described in §12.2.1 of the Scala Reference (March 15,
2009, the one offered from the website right now) looks pretty simple
and intuitive to me, however, it fails to explain the observation made
by Paul above. Is there an updated version available? Or did I look at
the wrong section? This is a tricky area and it would be most
appreciated if there were a complete and compact description of the
rules available in one central place.

Regards,

Roland

--
I'm a physicist: I have a basic working knowledge of the universe and
everything it contains!
- Sheldon Cooper (The Big Bang Theory)

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: nobody ever tires of equality

On Wed, Nov 4, 2009 at 9:23 AM, Roland Kuhn wrote:
> On Oct 27, 2009, at 09:39 , martin odersky wrote:
>
>> On Mon, Oct 26, 2009 at 10:28 PM, Paul Phillips
>> wrote:
>>>
>>> Here's a whole separate category of perversion which comes up under
>>> proposed 2.8 equality.  It's nice that I'm still finding new ones, I'd
>>> hate for it all to seem dreary and ho-hum.
>
> [snip]
>>>
>>> // this is false because it is comparing (1: Any) and (1L: Any)
>>> scala> (s1.xs zip s2.xs) forall Function.tupled(_ == _)
>>> res1: Boolean = false
>
> [snip]
>>>
>>> scala> (s3.xs zip s4.xs) forall Function.tupled(_ == _)
>>> res3: Boolean = true
>>>
>>> ...we're comparing (1: Int) and (1: Long) which is true.
>>>
>> I think all this just shows that static overloading resolution can be
>> subtle because you need to anticipate the result of type inference.
>> But none of that is new. Users deal with that usually in that they
>> stay away from situations that could be tricky. In our case, they
>> would probably be reluctant to mix elements of different primitive
>> types in one list or collection. That's just as well.
>>
>> Faced with a tricky situation like this, I think it's important to
>> pick the simplest rules possible. If you do that, there might be
>> observations which are surprising at first, but users are able to work
>> out in the end why things worked the way they did. Unless it is 100%
>> leak-free, a more complicated scheme would hurt rather than help.
>>
> The scheme described in §12.2.1 of the Scala Reference (March 15, 2009, the
> one offered from the website right now) looks pretty simple and intuitive to
> me, however, it fails to explain the observation made by Paul above. Is
> there an updated version available? Or did I look at the wrong section? This
> is a tricky area and it would be most appreciated if there were a complete
> and compact description of the rules available in one central place.
>
I'd have to see Paul's mail again in context to comment. Can you or
sombody else explain why the example is not covered by the rules of
the SLR?

Thanks

Roland Kuhn
Joined: 2008-12-26,
User offline. Last seen 3 years 14 weeks ago.
Re: nobody ever tires of equality

On Nov 4, 2009, at 10:23 , martin odersky wrote:
> On Wed, Nov 4, 2009 at 9:23 AM, Roland Kuhn wrote:
>> On Oct 27, 2009, at 09:39 , martin odersky wrote:
>>
>>> On Mon, Oct 26, 2009 at 10:28 PM, Paul Phillips
>>>
>>> wrote:
>>>>
>>>> Here's a whole separate category of perversion which comes up under
>>>> proposed 2.8 equality. It's nice that I'm still finding new
>>>> ones, I'd
>>>> hate for it all to seem dreary and ho-hum.
>>
>> [snip]
>>>>
>>>> // this is false because it is comparing (1: Any) and (1L: Any)
>>>> scala> (s1.xs zip s2.xs) forall Function.tupled(_ == _)
>>>> res1: Boolean = false
>>
>> [snip]
>>>>
>>>> scala> (s3.xs zip s4.xs) forall Function.tupled(_ == _)
>>>> res3: Boolean = true
>>>>
>>>> ...we're comparing (1: Int) and (1: Long) which is true.
>>>>
>>> I think all this just shows that static overloading resolution can
>>> be
>>> subtle because you need to anticipate the result of type inference.
>>> But none of that is new. Users deal with that usually in that they
>>> stay away from situations that could be tricky. In our case, they
>>> would probably be reluctant to mix elements of different primitive
>>> types in one list or collection. That's just as well.
>>>
>>> Faced with a tricky situation like this, I think it's important to
>>> pick the simplest rules possible. If you do that, there might be
>>> observations which are surprising at first, but users are able to
>>> work
>>> out in the end why things worked the way they did. Unless it is 100%
>>> leak-free, a more complicated scheme would hurt rather than help.
>>>
>> The scheme described in §12.2.1 of the Scala Reference (March 15,
>> 2009, the
>> one offered from the website right now) looks pretty simple and
>> intuitive to
>> me, however, it fails to explain the observation made by Paul
>> above. Is
>> there an updated version available? Or did I look at the wrong
>> section? This
>> is a tricky area and it would be most appreciated if there were a
>> complete
>> and compact description of the rules available in one central place.
>>
> I'd have to see Paul's mail again in context to comment. Can you or
> sombody else explain why the example is not covered by the rules of
> the SLR?
>
After re-reading §12.2.1 more carefully, I find that the case "(1:
Any) == (1L: Any)" is not specified. The description of "==" pertains
only to statically known AnyVal instances, while the pseudo-code given
for equals (which would AFAICS mandate a return of "true" for the
example above) does not say anything about "==", unless I'm mistaken.
Thus, to state my point less ambiguously, it would be useful to have a
specification for the dynamic behavior of "==" on AnyVal in addition
to the defined static behavior. My proposal, following the principle
of least surprise, would be to match the two.

Regards,

Roland

--
Simplicity and elegance are unpopular because they require hard work
and discipline to achieve and education to be appreciated.

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: nobody ever tires of equality

On Wed, Nov 4, 2009 at 11:54 AM, Roland Kuhn wrote:
> On Nov 4, 2009, at 10:23 , martin odersky wrote:
>>
>> On Wed, Nov 4, 2009 at 9:23 AM, Roland Kuhn wrote:
>>>
>>> On Oct 27, 2009, at 09:39 , martin odersky wrote:
>>>
>>>> On Mon, Oct 26, 2009 at 10:28 PM, Paul Phillips
>>>> wrote:
>>>>>
>>>>> Here's a whole separate category of perversion which comes up under
>>>>> proposed 2.8 equality.  It's nice that I'm still finding new ones, I'd
>>>>> hate for it all to seem dreary and ho-hum.
>>>
>>> [snip]
>>>>>
>>>>> // this is false because it is comparing (1: Any) and (1L: Any)
>>>>> scala> (s1.xs zip s2.xs) forall Function.tupled(_ == _)
>>>>> res1: Boolean = false
>>>
>>> [snip]
>>>>>
>>>>> scala> (s3.xs zip s4.xs) forall Function.tupled(_ == _)
>>>>> res3: Boolean = true
>>>>>
>>>>> ...we're comparing (1: Int) and (1: Long) which is true.
>>>>>
>>>> I think all this just shows that static overloading resolution can be
>>>> subtle because you need to anticipate the result of type inference.
>>>> But none of that is new. Users deal with that usually in that they
>>>> stay away from situations that could be tricky. In our case, they
>>>> would probably be reluctant to mix elements of different primitive
>>>> types in one list or collection. That's just as well.
>>>>
>>>> Faced with a tricky situation like this, I think it's important to
>>>> pick the simplest rules possible. If you do that, there might be
>>>> observations which are surprising at first, but users are able to work
>>>> out in the end why things worked the way they did. Unless it is 100%
>>>> leak-free, a more complicated scheme would hurt rather than help.
>>>>
>>> The scheme described in §12.2.1 of the Scala Reference (March 15, 2009,
>>> the
>>> one offered from the website right now) looks pretty simple and intuitive
>>> to
>>> me, however, it fails to explain the observation made by Paul above. Is
>>> there an updated version available? Or did I look at the wrong section?
>>> This
>>> is a tricky area and it would be most appreciated if there were a
>>> complete
>>> and compact description of the rules available in one central place.
>>>
>> I'd have to see Paul's mail again in context to comment. Can you or
>> sombody else explain why the example is not covered by the rules of
>> the SLR?
>>
> After re-reading §12.2.1 more carefully, I find that the case "(1: Any) ==
> (1L: Any)" is not specified.

It's specified in the definition of class Any. Here's the relevant code:

abstract class Any {

/** Defined equality; abstract here */
def equals(that: Any): Boolean

/** Semantic equality between values */
final def == (that: Any): Boolean =
if (null eq this) null eq that else this equals that
...

}

Cheers

Roland Kuhn
Joined: 2008-12-26,
User offline. Last seen 3 years 14 weeks ago.
Re: nobody ever tires of equality

On Nov 4, 2009, at 12:01 , martin odersky wrote:
> On Wed, Nov 4, 2009 at 11:54 AM, Roland Kuhn wrote:
>> On Nov 4, 2009, at 10:23 , martin odersky wrote:
>>>
>>> On Wed, Nov 4, 2009 at 9:23 AM, Roland Kuhn wrote:
>>>>
>>>> On Oct 27, 2009, at 09:39 , martin odersky wrote:
>>>>
>>>>> On Mon, Oct 26, 2009 at 10:28 PM, Paul Phillips >>>> >
>>>>> wrote:
>>>>>>
>>>>>> Here's a whole separate category of perversion which comes up
>>>>>> under
>>>>>> proposed 2.8 equality. It's nice that I'm still finding new
>>>>>> ones, I'd
>>>>>> hate for it all to seem dreary and ho-hum.
>>>>
>>>> [snip]
>>>>>>
>>>>>> // this is false because it is comparing (1: Any) and (1L: Any)
>>>>>> scala> (s1.xs zip s2.xs) forall Function.tupled(_ == _)
>>>>>> res1: Boolean = false
>>>>
>>>> [snip]
>>>>>>
>>>>>> scala> (s3.xs zip s4.xs) forall Function.tupled(_ == _)
>>>>>> res3: Boolean = true
>>>>>>
>>>>>> ...we're comparing (1: Int) and (1: Long) which is true.
>>>>>>
>>>>> I think all this just shows that static overloading resolution
>>>>> can be
>>>>> subtle because you need to anticipate the result of type
>>>>> inference.
>>>>> But none of that is new. Users deal with that usually in that they
>>>>> stay away from situations that could be tricky. In our case, they
>>>>> would probably be reluctant to mix elements of different primitive
>>>>> types in one list or collection. That's just as well.
>>>>>
>>>>> Faced with a tricky situation like this, I think it's important to
>>>>> pick the simplest rules possible. If you do that, there might be
>>>>> observations which are surprising at first, but users are able
>>>>> to work
>>>>> out in the end why things worked the way they did. Unless it is
>>>>> 100%
>>>>> leak-free, a more complicated scheme would hurt rather than help.
>>>>>
>>>> The scheme described in §12.2.1 of the Scala Reference (March 15,
>>>> 2009,
>>>> the
>>>> one offered from the website right now) looks pretty simple and
>>>> intuitive
>>>> to
>>>> me, however, it fails to explain the observation made by Paul
>>>> above. Is
>>>> there an updated version available? Or did I look at the wrong
>>>> section?
>>>> This
>>>> is a tricky area and it would be most appreciated if there were a
>>>> complete
>>>> and compact description of the rules available in one central
>>>> place.
>>>>
>>> I'd have to see Paul's mail again in context to comment. Can you or
>>> sombody else explain why the example is not covered by the rules of
>>> the SLR?
>>>
>> After re-reading §12.2.1 more carefully, I find that the case "(1:
>> Any) ==
>> (1L: Any)" is not specified.
>
> It's specified in the definition of class Any. Here's the relevant
> code:
>
> abstract class Any {
>
> /** Defined equality; abstract here */
> def equals(that: Any): Boolean
>
> /** Semantic equality between values */
> final def == (that: Any): Boolean =
> if (null eq this) null eq that else this equals that
> ...
>
> }
>
Oh, sorry for the noise: I just tried Paul's original example on the
latest nightly build and it returns "true" as expected. The "false"
from the OP was irritating me. Thanks for your patience.

Regards,

Roland

--
[scala-debate on 2009/10/2]
Viktor Klang: When will the days of numerical overflow be gone?
Ricky Clarkson: One second after 03:14:07 UTC on Tuesday, 19 January
2038

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland