In Scala 3, with -language:strictEquality
, this code:
trait X[A]:
def val1: A
def val2: A
def f() =
if val1 == val2 then
println("Same")
produces this error:
Values of types A and A cannot be compared with == or !=
I looked for some trait that would tell the compiler that A
can be compared to itself, but couldn't find one. The solution I found was to add this line:
given CanEqual[A, A] = CanEqual.derived
However, I still have several questions:
- How come you can't compare two instances of the same type? What are some instances of
A
whereval1 == val2
would not be legal? - Is there a trait that would add
==
,!=
to a type? - If the compiler can't figure out that you can compare
A
toA
whenA
is a type parameter, then how comegiven CanEqual[A, A] = CanEqual.derived
keeps it happy? What is the magic inCanEqual.derived
and why can't it be applied by default when using==
?
CodePudding user response:
This comparison is not really illegal, it is just undefined.
The
given
solution is more flexible than atrait
, but you can just define==
on your type if you prefer.You have explicitly told the compiler that you don't want it to generate a default
==
operator. That's whatstrictEquality
means.
The logic behind strictEquality
is that there are different kinds of equality and the compiler shouldn't arbitrarily chose one kind. Objects can be compared by reference or by contents, and it neither is "correct" in every case.
Without strictEquality
the compiler implements referential equality by default, which means two values are the same only if they refer to the same object. case class
implements structural equality by checking each field for equality, so two different instances are "equal" if all the values of all the fields are the same. Other classes can implement equality by comparing only a relevant subset of field, or by other criteria.
According to the documentation strict equality is opt-in for for compatibility and migration reasons.
CodePudding user response:
Without -language:strictEquality
, these ==
calls are actually handled by equals(Object other)
method defined on Java Object
class.
So, it used either the overridden
equals
method for your class or the defualt equals
from Object class. This often results in unexpected behaviours.
Adding -language:strictEquality
changes this behaviour, and ==
are handled using the typeclass CanEqual[A, A]
.
To ensure that you are using correct equality, the design decision was to force you to explicitly provide that CanEqual[A, A]
.
Now, there is no default catch all way to compare two values. If you want anything to be compared, be ready to provide how to comapre as well.
The comparision is not illegal in any way, but the language just makes no assumptions on how two values should be compared.