Case classes and Proxy behaviour in Scala 2.9
Asked Answered
N

3

8

On migrating our code to Scala 2.9 we've found large swathes of it that didn't work and failed silently. We tracked it down to case classes that extend Proxy not being equal. In our code we don't extend Proxy directly, we just extend classes in libraries that extend Proxy.

Any help would be greatly appreciated.

In 2.8

scala> case class Test(a:String) extends Proxy {
     |   def self = a
     | }
defined class Test

scala> 

scala> val label = new Test("bla")
label: Test = bla

scala> println(label == label) // this is TRUE
true

scala> println(label == "bla")
true

In 2.9

scala> case class Test(a:String) extends Proxy {
     |   def self = a
     | }
defined class Test

scala> 

scala> val label = new Test("bla")
label: Test = bla

scala> println(label == label) // this is now FALSE
false

scala> println(label == "bla")
true

Update

I think this can only be a bug in Scala 2.9. Otherwise if you have a case class that extends any other class you have to investigate that base class's hierarchy to make sure at no point is it extending Proxy. We won't be able to do this in our code, we'll just be able to fix the more obvious bugs. If this is intended behaviour then a compiler warning is a must. Does that sound about right?

Update

Also being discussed on the scala mailing list.

Update

I've filed a bug

Nucleate answered 12/7, 2011 at 16:27 Comment(4)
Did you end up reporting this as a bug? (If so, link?) The documentation for Proxy does warn you that it may lead to equals not being symmetric, but it doesn't warn you equals might not even be reflexive...!Mellman
Seth, I agree, this can only really be a bug. I've updated with a link above.Nucleate
The bug was reported fixed on 10/Sep/11, fyi.Visayan
Yeah but I'm waiting for it to be assigned to a version of Scala before I celebrate. It's been fixed a while but has no sign of being released yet.Nucleate
R
6

In 2.9 they changed the equals method from:

override def equals(that: Any): Boolean = 
  if(that == null) false 
  else that equals self

to

override def equals(that: Any): Boolean = that match {
 case null       => false
 case x: Equals  => (x canEqual self) && (x equals self)
 case x          => (x equals self)
}

x: Equals doesn't equal self for some reason.

You can override the equals method to fix it.

Rock answered 12/7, 2011 at 16:54 Comment(2)
I think the easiest fix is just to change the case class to a class for us. However this seems broken, or at least a behaviour change that should have been documented. ThanksNucleate
I think this is probably a bug, but this answer explains where the bug originated so I'll accept it. Thanks Joe.Nucleate
S
1

This will solve your problem

case class Test(a: String) extends Proxy {
   def self = a
   def canEqual(that: Any) = that match {
      case that: String => true
      case _ => false
   }
}
Scabrous answered 12/7, 2011 at 16:59 Comment(3)
Or, more simply, that.isInstanceOf[String].Pilsner
Also, note that this means label == "blah" will return true.Pilsner
It returned true in 2.8 according to the results in the question. I think the intent was to replicate the behavior of 2.8.Rock
U
0

So why don't you overwrite the equals method? That should solve the problem.

Unfurl answered 12/7, 2011 at 16:51 Comment(2)
By using a case class you should be getting an equals method that works. Using a case class and overriding the equals method seems a bit crazy, I could just not use a case class, which would work. It's just our code is full of them.Nucleate
Sure this is not ideal, but I don't see another way.Unfurl

© 2022 - 2024 — McMap. All rights reserved.