Deriving `CanEqual` succeeds on function when it shouldn't

Viewed 102

I'm a beginner with Scala, but I'm wondering why the following compiles in Scala 3?

import scala.language.strictEquality

case class C(x: String => String) derives CanEqual

Shouldn't this fail because CanEqual is not defined on String => String and hence can't be derived?

1 Answers

No it shouldn't.

Simply speaking, with derives CanEqual here you just give the compiler know that it's legal to use == or != with instances of C. The compiler will add an instance of CanEqual for C itself.

The things are different with generic classes (type constructors), as with CanEqual derives CanEqual that requires presence of CanEqual instances for all type parameters. It's however a constraint that's a result of using this particular method of deriving instances. You can always provide it yourself and then even for generic classes you may not need CanEqual for type parameters. E.g. this seems to be working:

class C[T](t: T)
given CanEqual[C[_], C[_]] = CanEqual.derived
println(new C[Int](1) == new C[String]("1")) // false

As I said, it's just a green light for using == or !=, it doesn't construct/derive the comparison operation itself. equals() method is used, whatever is its implementation. So for your case it will use default case class equals() method and it will use reference comparison for function in x.

Worth noting that scala.language.strictEquality doesn't limit usage of equals()

More reading: https://docs.scala-lang.org/scala3/book/ca-multiversal-equality.html https://dotty.epfl.ch/docs/reference/contextual/multiversal-equality.html plus I find descriptions of this feature in "Programming in Scala" and "Programming Scala" new editions a bit easier to digest

Hope it helps a bit

Related