Scala 3:仅针对某些类型发生的平等编译错误
Scala 3 : Equality compilation error occurring only for certain types
我正在尝试了解 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 !="
- 即使不导入也会出现编译错误
scala.language.strictEquality
- 这在 Scala2 中编译没有任何错误
case 2.比较两个case 类:
case class Cat(catname: String)
case class Dog(dogname: String)
val d = Dog("Frank")
val c = Cat("Morris")
d == c // false, but it compiles
我知道我需要 import scala.language.strictEquality
以在案例 2 中强制执行多元宇宙平等。但是为什么case1不需要呢?
通用相等性仅适用于尚未定义 CanEqual
个实例的类型。如Multiversal Equality所述:
Even though canEqualAny
is not declared as given
, the compiler will still construct an canEqualAny
instance as answer to an implicit search for the type CanEqual[L, R]
, unless L
or R
have CanEqual
instances defined on them, or the language feature strictEquality
is enabled.
并且由于在伴随对象中已经为 String
定义了一个 CanEqual
实例:
given canEqualString: CanEqual[String, String] = derived
通用相等性不适用于 String
或 Int
(根据@DmytroMitin,编译器会特别考虑 Scala 的数字类型),即使 strictEquality
已禁用。
注意
案例 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)
b) 问题是 CanEqual[-L, -R]
的类型参数 L
, R
之一是数值 class (Byte
, Short
, Char
, Int
, Long
, Float
, Double
)的处理方式不同:
val synthesizedCanEqual: SpecialHandler = (formal, span) =>
...
if canComparePredefined(arg1, arg2)
|| !Implicits.strictEquality && explore(validEqAnyArgs(arg1, arg2))
...
注意,这里如果答案是由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
请注意,如果 L
、R
中的一个是数值 class,那么另一个也必须是数值(考虑到装箱),这样 summon[CanEqual[Int, Int]]
, summon[CanEqual[Double, Double]]
, summon[CanEqual[Int, Double]]
编译但 summon[CanEqual[Int, String]]
不编译。
我正在尝试了解 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 !="
- 即使不导入也会出现编译错误
scala.language.strictEquality
- 这在 Scala2 中编译没有任何错误
case 2.比较两个case 类:
case class Cat(catname: String)
case class Dog(dogname: String)
val d = Dog("Frank")
val c = Cat("Morris")
d == c // false, but it compiles
我知道我需要 import scala.language.strictEquality
以在案例 2 中强制执行多元宇宙平等。但是为什么case1不需要呢?
通用相等性仅适用于尚未定义 CanEqual
个实例的类型。如Multiversal Equality所述:
Even though
canEqualAny
is not declared asgiven
, the compiler will still construct ancanEqualAny
instance as answer to an implicit search for the typeCanEqual[L, R]
, unlessL
orR
haveCanEqual
instances defined on them, or the language featurestrictEquality
is enabled.
并且由于在伴随对象中已经为 String
定义了一个 CanEqual
实例:
given canEqualString: CanEqual[String, String] = derived
通用相等性不适用于 String
或 Int
(根据@DmytroMitin,编译器会特别考虑 Scala 的数字类型),即使 strictEquality
已禁用。
注意
案例 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)
b) 问题是 CanEqual[-L, -R]
的类型参数 L
, R
之一是数值 class (Byte
, Short
, Char
, Int
, Long
, Float
, Double
)的处理方式不同:
val synthesizedCanEqual: SpecialHandler = (formal, span) =>
...
if canComparePredefined(arg1, arg2)
|| !Implicits.strictEquality && explore(validEqAnyArgs(arg1, arg2))
...
注意,这里如果答案是由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
请注意,如果 L
、R
中的一个是数值 class,那么另一个也必须是数值(考虑到装箱),这样 summon[CanEqual[Int, Int]]
, summon[CanEqual[Double, Double]]
, summon[CanEqual[Int, Double]]
编译但 summon[CanEqual[Int, String]]
不编译。