パターンマッチングにおけるメソッドの型推論とクラス型パラメーターの違い


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

質問はダニエルの分析に基づいています。私はこれに似た質問への回答を提供しようとしていました。

回答:


4

100%完全な答えはありませんが、あなたには十分かもしれないポインタがあります。

Scalaコンパイラーは、非常に特殊な方法でGADT(一般化代数データ型)を扱います。特別な処理で解決される場合もあれば、未解決の場合もあります。Dottyはほとんどの穴を埋めようと試みており、すでに多くの関連する問題を解決していますが、まだかなりの数の解決の問題もあります。

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の処理に関する情報源は、いくつか ブログ投稿に分類されますが、それはすばらしいことですが、それ以上の機能が必要な場合は、コンパイラーのコードを自分で掘り下げる必要があります。私はそれを正確に試してみましたが、これはこの方法に起因すると思いますが、本当に深く知りたい場合は、Scalaコンパイラのコードベースの経験が豊富な人にpingを送信することをお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.