Cannot match Scala 3 enum with scala.language.strictEquality

When setting the:

scala.language.strictEquality

flag and using case matching in Scala 3.1.2, I get:

Values of types sim.Level and sim.Level cannot be compared with == or !=

I am using a simple enum (see code below). Is this correct behavior? Shouldn’t this work automatically? What is the correct way to make this work? I used enum Level derives CanEqual to solve this, but this seems “forced”.

TIA,

enum Level:
  case OFF, INFO, DEBUG, WARN, ERROR

inline def log(s: String)(color: String): Unit =
  println(s"${color}$s${RESET}")

inline def log(s: String)(level: Level): Unit =
  inline level match
    case Level.OFF   => ()
    case Level.DEBUG => log(s)(MAGENTA)
    case Level.INFO  => log(s)(CYAN   )
    case Level.WARN  => log(s)(YELLOW )
    case Level.ERROR => log(s)(RED    )

It looks like desired behavior. You need to derive CanEqual when using strictEquality. When using strictEquality you are trading off one type of compiler error for another one. I’d recommend reading “Programming in Scala 5th Edition” Chapter 23 Typeclasses. Here is an excerpt:

For example, imagine your existing Scala 2 project contains a class Apple , defined like this:

case class Apple(size: Int)

And somewhere in your code you have been comparing two apples, like this:

val appleTwo = Apple(2)
val appleTwoToo = Apple(2)
appleTwo == appleTwoToo // true

This comparison would continue to compile and work under Scala 3 by de-
fault, because the left and right sides are the same type. However, because
the Scala 3 compiler will also still by default allow comparisons of types for
which no given reflexive CanEqual instances exist, the following undesirable
equality comparison would still compile:

case class Orange(size: Int)
val orangeTwo = Orange(2)
appleTwo == orangeTwo // false

This comparison likely represents a bug, because the result of compar-
ing any Apple to any Orange will always be false. To get full sanity check-
ing of all equality comparisons in Scala 3, even if no reflexive instances
have been defined for the involved types, you can enable “strict equality.”
You do so by either specifying a command line option to the compiler,
-language:strictEquality , or including the following import in your
source file:

import scala.language.strictEquality

With strict equality enabled, you will now get the desired compiler error
when comparing apples and oranges:

scala> appleTwo == orangeTwo
1 |appleTwo == orangeTwo
|ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ
|Values of types Apple and Orange cannot be
|
compared with == or !=

Unfortunately, you will now also get an undesired compiler error for your
desired comparison of apples to apples:

scala> appleTwo == appleTwoToo
1 |appleTwo == appleTwoToo
|ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ
|Values of types Apple and Apple cannot be
|
compared with == or !=

To enable this comparison under strict equality, you will need to provide
a CanEqual instance that gives permission for apples to be compared for
equality with other apples:

case class Apple(size: Int) derives CanEqual
1 Like

@spamegg1 Thanks for the detailed explanation. Initially I assumed that strict comparison avoided the apples to oranges comparison. I also assumed, incorrectly, that apples to apples comparison would also work. Seems a little to strict for me though.

I will use the CanEqual then.

Thanks again.

1 Like