模式匹配中方法类型推断与类类型参数的区别


9

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

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
    }
  }
}

这个问题基于Daniel的分析,我曾经尝试为类似的问题提供答案

Answers:


4

我没有100%完整的答案,但是我有一个可能足以满足您要求的指针。

Scala编译器以非常特殊的方式处理GADT(广义代数数据类型)。有些情况下需要特殊处理才能解决,有些情况下无法解决。斑点狗正在试图填补大部分的孔,并且它已经解决了很多相关的问题,但仍然有相当多的开放的以及。

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已被“固定”为a Int(因为我们匹配的Derived是a Base[Int])。

A必须在某处声明GADT的通用类型参数(在我们的示例中为)。这是有趣的部分,特殊编译器处理仅在将其声明为封闭方法的类型参数时才起作用。如果它来自封闭的特征/类的类型成员或类型参数,则无法编译,正如您亲眼所见。

这就是为什么我说这不是100%完整的答案-我无法指出正确记录此问题的具体位置(例如官方规格)。在斯卡拉处理GADTs的来源归结为一对夫妇 相关博客文章,它的方式是伟大的,但如果你想更重要的是,你必须深入到代码编译自己。我尝试过完全做到这一点,并且我认为这取决于这种方法,但是如果您真的想更深入一点,则可能想ping通使用Scala编译器代码库有经验的人。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.