如何强制Scala匹配类型在编译时完全规约?
Scala匹配类型不完全规约的原因分析
这不是编译器bug,而是Scala匹配类型的惰性规约设计特性——匹配类型的类型推导(规约)并非在所有场景下都会自动触发,只有当编译器需要明确的、可判定的类型信息时,才会尝试完成规约。
示例拆解分析
先看你的代码示例:
sealed trait AB case class A() extends AB case class B() extends AB type OnlyA[X] = X match case A => A def onlyA[X <: Matchable](x: X): OnlyA[X] = x match case a: A => a val isA: OnlyA[A] = onlyA(A()) val isB: OnlyA[B] = onlyA(B()) // fails as expected val isAorB: OnlyA[AB] = onlyA(B(): AB) // 编译通过,未触发规约 val ev = summon[OnlyA[AB] =:= A] // 触发规约,编译失败
isB编译失败的原因:
当X被推断为具体类型B时,OnlyA[B]会立即触发规约。由于B不匹配case A分支,且没有其他分支兜底,编译器判定该匹配类型无有效实例,直接报错,符合预期。isAorB编译通过的原因:
这里X是抽象超类型AB(密封特质),编译器不会主动枚举所有子类型去尝试规约OnlyA[AB]——因为AB的实例可能是A或B,编译器无法在此时确定唯一的匹配结果,且主动枚举复杂类型层级会带来性能开销。此时OnlyA[AB]被视为一个未规约的匹配类型,而onlyA(B(): AB)的返回值类型正好是这个未规约类型,左右类型兼容,因此编译通过。summon[OnlyA[AB] =:= A]编译失败的原因:
当需要证明OnlyA[AB]和A类型等价时,编译器必须触发OnlyA[AB]的规约。此时它会枚举AB的所有子类型:- 若
X是A,OnlyA[A]规约为A; - 若
X是B,OnlyA[B]无有效分支,无法规约。
由于存在子类型无法匹配到有效分支,OnlyA[AB]无法被规约为单一确定的类型,因此类型相等性检查失败,符合预期。
- 若
匹配类型的核心规约规则
- 具体类型触发立即规约:当类型参数是明确的非抽象类型(如
A、B),编译器会直接完成匹配类型的规约。 - 抽象/超类型惰性规约:当类型参数是抽象类型或超类型时,匹配类型会保持未规约状态,直到遇到需要明确类型信息的场景(如类型相等性检查、显式类型转换、summon类型证据等)。
- 密封类型层级的特殊处理:只有当密封类型的所有子类型都能匹配到同一个分支时,匹配类型才会被规约为该分支的类型;若存在子类型无匹配分支或匹配不同分支,匹配类型会保持未规约。
内容的提问来源于stack exchange,提问作者user3508638
相关产品推荐
相关产品推荐

