Mar*_*lic 9 scala type-inference pattern-matching type-parameter
当类型参数来自封闭方法而不是封闭类时,为什么模式匹配的工作方式不同?例如,
trait Base[T]
case class Derived(v: Int) extends Base[Int]
class Test[A] {
def method(arg: Base[A]) = {
arg match {
case Derived(_) => 42
}
}
}
Run Code Online (Sandbox Code Playgroud)
给出错误
constructor cannot be instantiated to expected type;
found : A$A87.this.Derived
required: A$A87.this.Base[A]
case Derived(_) => 42
^
Run Code Online (Sandbox Code Playgroud)
虽然它成功编译时A是方法类型参数
class Test {
def method[A](arg: Base[A]) = {
arg match {
case Derived(_) => 42
}
}
}
Run Code Online (Sandbox Code Playgroud)
我没有 100% 完整的答案,但我有一个可能对你来说足够了。
Scala 编译器以非常特殊的方式处理 GADT(广义代数数据类型)。有的案件通过特殊处理得到解决,有的案件没有解决。Dotty 正在尝试填补大部分漏洞,它已经解决了很多相关问题,但仍然存在不少未解决的问题。
Scala 2 编译器中特殊 GADT 处理的典型示例与您的用例非常相关。如果我们看一下:
def method[A](arg: Base[A]) = {
arg match {
case Derived(_) => 42
}
}
Run Code Online (Sandbox Code Playgroud)
我们明确声明返回类型为A:
def method[A](arg: Base[A]): A
Run Code Online (Sandbox Code Playgroud)
它会编译得很好。您的 IDE 可能会抱怨,但编译器会让它通过。方法说它返回 an A,但模式匹配情况计算结果为 an Int,理论上不应编译。然而,编译器中对 GADT 的特殊处理表明这很好,因为在该特定模式匹配分支A已被“固定”为 an Int(因为我们匹配的Derived是 a Base[Int])。
GADT 的通用类型参数(在我们的例子中A)必须在某处声明。这是有趣的部分 - 特殊的编译器处理仅在声明为封闭方法的类型参数时才有效。如果它来自封闭特征/类的类型成员或类型参数,则它不会编译,正如您亲眼所见。
这就是为什么我说这不是 100% 完整的答案 - 我无法指出正确记录这一点的具体位置(例如官方规范)。有关在 Scala 中处理 GADT 的资料可以归结为几篇博 文,顺便说一句,这些博 文都很棒,但如果您想要更多内容,则必须自己深入研究编译器代码。我尝试这样做,我认为这归结为这种方法,但如果你真的想更深入,你可能想找一些对 Scala 编译器代码库更有经验的人。
| 归档时间: |
|
| 查看次数: |
195 次 |
| 最近记录: |