Scala 3:仅针对某些类型发生相等编译错误

Rem*_*oon 5 scala scala-3

我试图理解 Scala3 新的“多元平等”功能。在比较不同类型时,我遇到了不一致的行为。

情况 1. 比较 Int 和 String:

  val x = 1
  val y = "One"

  x == y  // gives compilation error -> "Values of types Int and String cannot be compared with == or !="
Run Code Online (Sandbox Code Playgroud)
  • 即使不导入也编译错误scala.language.strictEquality
  • 在 Scala2 中编译没有任何错误

案例 2. 比较两个案例类:

  case class Cat(catname: String)
  case class Dog(dogname: String)
  val d = Dog("Frank")
  val c = Cat("Morris")

  d == c  // false, but it compiles
Run Code Online (Sandbox Code Playgroud)

我知道我需要import scala.language.strictEquality在 case2 中强制执行多重平等。但为什么 case1 不需要呢?

Dmy*_*tin 3

请注意

情况1。summon[CanEqual[Int, String]]即使不导入也无法编译scala.language.strictEquality

案例2.summon[CanEqual[Cat, Dog]]

  • 无需导入即可编译,scala.language.strictEquality但是
  • 不会通过此类导入进行编译。

a) 类型 class 的实例CanEqual由编译器生成(以及scala.reflect.ClassTag, scala.reflect.TypeTest, scala.ValueOf, scala.deriving.Mirror.Product, scala.deriving.Mirror.Sum, scala.deriving.Mirror

  val specialHandlers = List(
    defn.ClassTagClass        -> synthesizedClassTag,
    defn.TypeTestClass        -> synthesizedTypeTest,
    defn.CanEqualClass        -> synthesizedCanEqual,
    defn.ValueOfClass         -> synthesizedValueOf,
    defn.Mirror_ProductClass  -> synthesizedProductMirror,
    defn.Mirror_SumClass      -> synthesizedSumMirror,
    defn.MirrorClass          -> synthesizedMirror)
Run Code Online (Sandbox Code Playgroud)

https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L489-L499

b) 问题在于,当类型参数L, Rof之一CanEqual[-L, -R]是数值类 ( Byte, Short, Char, Int, Long, Float, Double) 时,处理方式有所不同:

  val synthesizedCanEqual: SpecialHandler = (formal, span) =>
    ...
    if canComparePredefined(arg1, arg2)
        || !Implicits.strictEquality && explore(validEqAnyArgs(arg1, arg2))
    ...
Run Code Online (Sandbox Code Playgroud)

https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L147-L148

请注意,如果在此之前给出答案,则是否打开canComparePredefined并不重要。strictEquality

c)canComparePredefined调用

def canComparePredefinedClasses(cls1: ClassSymbol, cls2: ClassSymbol): Boolean =
  ...

  if cls1.isPrimitiveValueClass then
    if cls2.isPrimitiveValueClass then
      cls1 == cls2 || cls1.isNumericValueClass && cls2.isNumericValueClass
    else
      cmpWithBoxed(cls1, cls2)
  else if cls2.isPrimitiveValueClass then
    cmpWithBoxed(cls2, cls1)
  ...
  else
    false
Run Code Online (Sandbox Code Playgroud)

https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala#L108-L114

L请注意,这里如果,之一R是数值类,则另一个也必须是数值类(考虑装箱),以便summon[CanEqual[Int, Int]], summon[CanEqual[Double, Double]],summon[CanEqual[Int, Double]]编译但summon[CanEqual[Int, String]]不编译。