Hi
I have a very strange effect with == and equals. As far as I understand should both methods give the same result for objects that are not null. I will paste a session with the interactive shell from today: ---------------------------- scala> val a = 1 a: Int = 1 scala> val b = 1.0 b: Double = 1.0 scala> a == b res90: Boolean = true scala> b == a res91: Boolean = true scala> a equals b res92: Boolean = false scala> b equals a res93: Boolean = false ---------------------------- Am I wrong or is there something wrong? Best regards Uwe Schirmer Uwe Schirmer Software Engineer and Author E-Mail: [hidden email] |
On Sun, Jun 20, 2010 at 02:18:04AM +0200, [hidden email] wrote:
> I have a very strange effect with == and equals. As far as I > understand should both methods give the same result for objects that > are not null. ...except primitives. > Am I wrong or is there something wrong? You are incorrect. I don't know if the spec has been updated. With the exception of a couple corner case issues, comparing two primitives (boxed or unboxed) with == should always give the result you would have gotten by comparing those values as unboxed primitives. When you call equals directly, you are skipping all that softening logic and instead treated to java's theory that two boxed values of different types are always unequal. -- Paul Phillips | The most dangerous man to any government is the man who Protagonist | is able to think things out [...] Almost inevitably he Empiricist | comes to the conclusion that the government he lives under ha! spill, pupil | is dishonest, insane, intolerable. -- H. L. Mencken |
FYI here is a summary of ==/equals I sent to -internals on Apr 13. I
didn't get a response on this but I'm pretty sure it's in the ballpark of accurate since I implemented most of it. From: Paul Phillips <[hidden email]> To: martin odersky <[hidden email]> Cc: [hidden email] Here is a kind of off the top of my head attempt to spec out equality and hash codes. I don't really speak spec-ese but this is written in the pidgin spec-ese within my grasp. Does this look approximately correct? (Anyone else feel free to chime in on that point.) What if anything would you like me to do with it? Resolution of x == y ==================== 1) Null values will not cause NPEs. 2) Nothing is == to null except null. 3) All objects must be == to themselves. The first three conditions are summarized in this initial expansion of 'x == y', which the compiler may or may not inline. All user-defined equals methods are responsible for preserving invariants 2 and 3. if (x eq y) true else if (x eq null) false else // remainder of algorithm 4) If the static type of the left hand side allows for the possibility that it is a boxed or unboxed primitive numeric type (any of Byte, Short, Int, Long, Float, Double, or Char) then: go to step 5. If the static type definitively excludes those types, then: the result is x.equals(y). 5) If the static types of both operands are primitive types, then: the result is that of the primitive comparison, exactly as performed in java. If the static types are identical final types (for instance, both are java.lang.Longs) then the result is x.equals(y). In all other cases, both operands are boxed if necessary and a method in BoxesRunTime is called. (The method will be semantically equivalent to BoxesRunTime.equals, but a different method may be chosen to avoid repeating the above tests.) BoxesRuntime.equals =================== All of the preceding logic is preserved, and then it proceeds as follows, where 'x' remains the left hand side operand and 'y' the right. 1) Runtime instance checks will be done to determine the types of the operands, with the following resolutions. (Resolutions represent the semantics, not necessarily the implementation.) 1a) If both sides of the comparison are boxed primitives, then they are unboxed and the primitive comparison is performed as in java. 1b) If 'x' is a class implementing the scala.math.ScalaNumber trait, then the result is x.equals(y). 1c) If 'x' is a boxed primitive and 'y' is a class implementing the scala.math.ScalaNumber trait, then the result is y.equals(x). 1d) Otherwise, the result is x.equals(y). hashCode and ## =============== The unification of primitives and boxed types in scala necessitates measures to preserve the equality contract: equal objects must have equal hash codes. To accomplish this a new method is introduced on Any: def ##: Int This method should be called in preference to hashCode by all scala software which consumes hashCodes. (One need not use or even be aware of it unless implementing something which depends on hashCodes -- to define an object's hashCode, overridding hashCode remains the mechanism.) The default implementation of ## is simply to call hashCode: def ##: Int = this.hashCode() In the case of numeric types however, it selectively alters hash codes to support the == algorithm given above. The guarantees provided by ## are as follows. "Numbers" are the aforementioned primitives (boxed or unboxed) and any standard scala library class implementing ScalaNumber. 1) If x and y are whole Numbers in the range Int.MinValue to Int.MaxValue, then (x == y) implies (x.## == y.##). The value of ## for all Numbers in that range is equal to the result of .toInt on that Number. 2) If x and y are Numbers and either or both is fractional, then the guarantee in 1) applies if both are in the range Short.MinValue to Short.MaxValue. 3) If x and y are Numbers and neither 1) nor 2) applies, the implication is preserved on a best-effort basis, but cannot be preserved generally given the fuzziness introduced in primitive equality at the borders. (For instance given a large Float, a java primitive Float/Double comparison may return true for 2^10 different Double values, and similar issues arise with Longs and Doubles.) -- Paul Phillips | Giving every man a vote has no more made men wise Moral Alien | and free than Christianity has made them good. Empiricist | -- H. L. Mencken slap pi uphill! |----------* http://www.improving.org/paulp/ *---------- |
Thank you, you are great.
I had a look in some of the Books about Scala and all stated that == is a test for null and calling equals. MAny thanks Uwe On Sat, 19 Jun 2010 17:33 -0700, "Paul Phillips" <[hidden email]> wrote: > FYI here is a summary of ==/equals I sent to -internals on Apr 13. I > didn't get a response on this but I'm pretty sure it's in the ballpark > of accurate since I implemented most of it. > > > From: Paul Phillips <[hidden email]> > To: martin odersky <[hidden email]> > Cc: [hidden email] > > Here is a kind of off the top of my head attempt to spec out equality > and hash codes. I don't really speak spec-ese but this is written in > the pidgin spec-ese within my grasp. Does this look approximately > correct? (Anyone else feel free to chime in on that point.) What if > anything would you like me to do with it? > > Resolution of x == y > ==================== > > 1) Null values will not cause NPEs. > > 2) Nothing is == to null except null. > > 3) All objects must be == to themselves. > > The first three conditions are summarized in this initial expansion of 'x > == y', which the compiler may or may not inline. All user-defined equals > methods are responsible for preserving invariants 2 and 3. > > if (x eq y) true > else if (x eq null) false > else // remainder of algorithm > > 4) If the static type of the left hand side allows for the possibility > that it is a boxed or unboxed primitive numeric type (any of Byte, Short, > Int, Long, Float, Double, or Char) then: go to step 5. > > If the static type definitively excludes those types, then: the result is > x.equals(y). > > 5) If the static types of both operands are primitive types, then: the > result is that of the primitive comparison, exactly as performed in java. > > If the static types are identical final types (for instance, both are > java.lang.Longs) then the result is x.equals(y). > > In all other cases, both operands are boxed if necessary and a method in > BoxesRunTime is called. (The method will be semantically equivalent to > BoxesRunTime.equals, but a different method may be chosen to avoid > repeating the above tests.) > > BoxesRuntime.equals > =================== > > All of the preceding logic is preserved, and then it proceeds as follows, > where 'x' remains the left hand side operand and 'y' the right. > > 1) Runtime instance checks will be done to determine the types of the > operands, with the following resolutions. (Resolutions represent the > semantics, not necessarily the implementation.) > > 1a) If both sides of the comparison are boxed primitives, then they are > unboxed and the primitive comparison is performed as in java. > > 1b) If 'x' is a class implementing the scala.math.ScalaNumber trait, then > the result is x.equals(y). > > 1c) If 'x' is a boxed primitive and 'y' is a class implementing the > scala.math.ScalaNumber trait, then the result is y.equals(x). > > 1d) Otherwise, the result is x.equals(y). > > hashCode and ## > =============== > > The unification of primitives and boxed types in scala necessitates > measures to preserve the equality contract: equal objects must have equal > hash codes. To accomplish this a new method is introduced on Any: > > def ##: Int > > This method should be called in preference to hashCode by all scala > software which consumes hashCodes. (One need not use or even be aware of > it unless implementing something which depends on hashCodes -- to define > an object's hashCode, overridding hashCode remains the mechanism.) > > The default implementation of ## is simply to call hashCode: > > def ##: Int = this.hashCode() > > In the case of numeric types however, it selectively alters hash codes to > support the == algorithm given above. The guarantees provided by ## are > as follows. "Numbers" are the aforementioned primitives (boxed or > unboxed) and any standard scala library class implementing ScalaNumber. > > 1) If x and y are whole Numbers in the range Int.MinValue to > Int.MaxValue, then (x == y) implies (x.## == y.##). The value of ## for > all Numbers in that range is equal to the result of .toInt on that > Number. > > 2) If x and y are Numbers and either or both is fractional, then the > guarantee in 1) applies if both are in the range Short.MinValue to > Short.MaxValue. > > 3) If x and y are Numbers and neither 1) nor 2) applies, the implication > is preserved on a best-effort basis, but cannot be preserved generally > given the fuzziness introduced in primitive equality at the borders. > (For instance given a large Float, a java primitive Float/Double > comparison may return true for 2^10 different Double values, and similar > issues arise with Longs and Doubles.) > > -- > Paul Phillips | Giving every man a vote has no more made men wise > Moral Alien | and free than Christianity has made them good. > Empiricist | -- H. L. Mencken > slap pi uphill! |----------* http://www.improving.org/paulp/ > *---------- > Software Engineer and Author E-Mail: [hidden email] |
In reply to this post by Paul Phillips-3
Just for the record. This is correct code !?
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20). Type in expressions to have them evaluated. Type :help for more information. scala> val no= Array.ofDim(1) no: Array[Nothing] = Array(null) scala> println(no(0)==null) true regards esser |
On Sun, Jun 20, 2010 at 02:02:10AM -0700, friedrich wrote:
> Just for the record. This is correct code !? > scala> val no= Array.ofDim(1) > no: Array[Nothing] = Array(null) Nothing should not be inhabited, even by null. This is a compiler bug. > scala> println(no(0)==null) > true Apart from the above, this is OK. Lauri |
In reply to this post by Paul Phillips-3
If the left-hand side is a null with a static type compatible with java.lang.Number and the right-hand side is a primitive, then I get an NPE. This contradicts the first rule above. Is this the expected behavior? The below is from 2.8.0.RC6: scala> null == 42 res0: Boolean = false scala> (null:java.lang.Integer) == 42 java.lang.NullPointerException at scala.runtime.BoxesRunTime.equalsNumNum(Unknown Source) at scala.runtime.BoxesRunTime.equalsNumObject(Unknown Source) scala> (null:java.lang.Integer) == new java.lang.Integer(42) res2: Boolean = false scala> (null:String) == 42 res3: Boolean = false scala> (null:java.lang.Integer) == "foo" res4: Boolean = false Thanks, Ross |
On Sun, Jun 20, 2010 at 07:54:44PM -0700, Ross A. Baker wrote:
> If the left-hand side is a null with a static type compatible with > java.lang.Number and the right-hand side is a primitive, then I get an > NPE. This contradicts the first rule above. Is this the expected > behavior? Very much a bug, which I will fix immediately. -- Paul Phillips | It's better to have gloved and tossed than never to Vivid | have played baseball. Empiricist | pal, i pill push |----------* http://www.improving.org/paulp/ *---------- |
Powered by Nabble | Edit this page |