【问题标题】:Difference between type inference of method and class type parameters in pattern matching模式匹配中方法和类类型参数类型推断的区别
【发布时间】:2020-03-05 21:19:21
【问题描述】:

为什么当类型参数来自封闭方法而不是封闭类时,模式匹配的工作方式不同?例如,

trait Base[T]
case class Derived(v: Int) extends Base[Int]

class Test[A] {
  def method(arg: Base[A]) = {
    arg match {
      case Derived(_) => 42
    }
  }
}

报错

constructor cannot be instantiated to expected type;
 found   : A$A87.this.Derived
 required: A$A87.this.Base[A]
      case Derived(_) => 42
           ^

A为方法类型参数时编译成功

class Test {
  def method[A](arg: Base[A]) = {
    arg match {
      case Derived(_) => 42
    }
  }
}

这个问题是基于丹尼尔的analysis,我曾经尝试将answer提供给类似的问题。

【问题讨论】:

    标签: scala pattern-matching type-inference type-parameter


    【解决方案1】:

    我没有 100% 完整的答案,但我有一个指针可能对你来说就足够了。

    Scala 编译器以一种非常特殊的方式处理 GADT(广义代数数据类型)。有些案件通过特殊处理解决了,有些案件没有解决。 Dotty 正在努力填补大部分漏洞,它已经有solved 很多相关的问题,但还有不少open 的问题。

    Scala 2 编译器中特殊 GADT 处理的典型示例与您的用例非常相关。如果我们看一下:

    def method[A](arg: Base[A]) = {
      arg match {
        case Derived(_) => 42
      }
    }
    

    我们明确声明返回类型为A

    def method[A](arg: Base[A]): A 
    

    它会编译得很好。您的 IDE 可能会抱怨,但编译器会让它通过。方法说它返回一个A,但模式匹配的情况评估为一个Int,理论上不应该编译。但是,编译器中对 GADT 的特殊处理表明这很好,因为在那个特定的模式匹配分支中,A 已“固定”为Int(因为我们在Derived 上匹配,这是一个Base[Int])。

    必须在某处声明 GADT 的通用类型参数(在我们的例子中为 A)。这是有趣的部分——特殊的编译器处理只有在它被声明为封闭方法的类型参数时才有效。如果它来自封闭特征/类的类型成员或类型参数,它不会编译,正如您亲眼所见。

    这就是为什么我说这不是一个 100% 完整的答案 - 我无法指出正确记录这一点的具体地方(例如官方规范)。在 Scala 中处理 GADT 的资源归结为 couple of blogposts,顺便说一句,这很棒,但如果您想要更多,您将不得不自己深入研究编译器代码。我尝试这样做,我认为它归结为this method,但如果你真的想更深入,你可能需要 ping 一个对 Scala 编译器代码库更有经验的人。

    【讨论】:

      猜你喜欢
      • 2012-09-02
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-09
      • 1970-01-01
      相关资源
      最近更新 更多