Reputation: 8462
Reading Martin's book, chapter about equality Object equality chapter one may notice that properly implementing equals
in scala (as actually in any other language) is not very straightforward. However scala is extremely powerful and agile, and I cannot believe it could not simplify things a bit. I know that scala generates proper equals
for case classes, so I wonder why couldn't it generate simplifications for normal classes?
To show my point, I wrote an example of how would I see this should look like. It probably has flaws, and I had to use ClassTag
which I know is very wrong for such basic thing as equals
due to performance (any tip how could I do it without ClassTag
?), but thinking that scala can generate proper equals
for case classes, I'd say it should be able to generate proper code for normal classes, giving that developer provides the Key
which should be used to compare objects.
trait Equality[T] extends Equals {
val ttag: ClassTag[T]
def Key: Seq[Any]
def canEqual(other: Any): Boolean = other match {
case that: Equality[_] if that.ttag == ttag => true
case _ => false
}
override def equals(other: Any): Boolean =
other match {
case that: Equality[T] => canEqual(that) && Key == that.Key
case _ => false
}
override def hashCode = Key.foldLeft(1)((x, y) => 41 * x + y.hashCode)
}
Then you can use it like this:
class Point(val x: Int, val y: Int)(implicit val ttag: ClassTag[Point]) extends Equality[Point]{
override def Key: Seq[Any] = Seq(x, y)
}
I'm not very much into ClassTags
, so I might have made it wrong, but it seems to be working. Still that's not what I am asking - I want to know is there any serious reasons, why scala itself do not simplify implementing equality checks?
Upvotes: 1
Views: 180
Reputation: 4323
It is an interesting idea, and I can see from a comment that scalaz has something like this. I'm not sure I have a complete answer, but some factors to consider are:
Case classes are a bit unique in that you're not supposed to inherit from them. Assuming they're not sub-classed, canEqual isn't even required.
There's something elegant about the idea that even '+' in scala is a function not a 'language feature.' It's not necessarily better to have this as a language feature instead of a library (and there are a number of utilities even in Java to help with implementing hashcode/equals). The existing "equals" method isn't a language feature, it's just a method on the parent class Object, inherited from Java.
Scala does still need to play-nice with Java, and that could be one barrier to radically changing how equals works. Interface requirements can't be imposed on existing Java classes that scala might want to inherit from.
When you do consider what the syntax might look like if it were a language feature, I'm not sure what could actually be eliminated or changed.
For example, the developer still has to specify the Key you suggest. Also, to be able to support, for example, anonymous subclasses and trait mix-ins where canEqual won't change, with a language feature you'd still need to explicitly define your class tag.
Maybe the analysis could be more interesting if you provide what, syntactically, it might look like if it were a language feature instead of a helper library. I might be missing some aspects of how this would work.
Upvotes: 1
Reputation: 1883
There is no such thing "proper equals" since it depends on the use cases.
For simple cases what you suggest may work, but in such case, using case class with also work. The problem is that while for simple Data classes it works, the same isn't true for other cases. For example, when there are inheritance - what is the right thing to do? if class have private members - does it should be part of the equals?? does public getters should be ? What will happen when when there are cyclic dependencies ?
One of the main reasons for the case classes cannot be inherit by other case class is the equality.
Upvotes: 1