16

Test.test のエラーは不当に思えます:

sealed trait A[-K, +V]
case class B[+V]() extends A[Option[Unit], V]

case class Test[U]() { 
  def test[V](t: A[Option[U], V]) = t match {
    case B() => null // constructor cannot be instantiated to expected type; found : B[V] required: A[Option[U],?V1] where type ?V1 <: V (this is a GADT skolem)
  }
  def test2[V](t: A[Option[U], V]) = Test2.test2(t)
}

object Test2 {
  def test2[U, V](t: A[Option[U], V]) = t match {
    case B() => null // This works
  }
}

エラーを変更する、または解消するには、いくつかの方法があります。

特性 A (およびケース クラス B) の V パラメータを削除すると、エラーの「GADT-skolem」部分はなくなりますが、「コンストラクタをインスタンス化できません」部分は残ります。

Test クラスの U パラメータを Test.test メソッドに移動すると、エラーはなくなります。なんで ?(同様に、エラーは Test2.test2 には存在しません)

次のリンクもその問題を特定していますが、提供された説明がわかりません。http://lambdalog.seanseefried.com/tags/GADTs.html

これはコンパイラのエラーですか? (2.10.2-RC2)

それを明確にするのを手伝ってくれてありがとう。


2014/08/05: コードをさらに単純化することに成功し、コンパイル エラーを発生させずに U が即時関数の外にバインドされる別の例を提供します。2.11.2 でもこのエラーが発生します。

sealed trait A[U]
case class B() extends A[Unit]

case class Test[U]() {
  def test(t: A[U]) = t match {
    case B() => ??? // constructor cannot be instantiated to expected type; found : B required: A[U]
  }
}

object Test2 {
  def test2[U](t: A[U]) = t match {
    case B() => ??? // This works
  }
  def test3[U] = {
    def test(t: A[U]) = t match {
      case B() => ??? // This works
    }
  }
}

単純化すると、これはコンパイラのバグまたは制限のように見えます。または、何か不足していますか?

4

5 に答える 5

12

コンストラクター パターンは、パターンの予想される型に準拠する必要があります。これは、B <: A[U] を意味し、U が現在呼び出されているメソッドの型パラメーターである場合に true であるという主張です (適切な型引数にインスタンス化できるため)。 ) ただし、U が以前にバインドされたクラス型パラメーターである場合は正しくありません。

「準拠しなければならない」というルールの価値について疑問を呈することは間違いありません。私は持っていることを知っています。通常、コンストラクターのパターンが適合するまで検査対象をアップキャストすることで、このエラーを回避します。具体的には、

// Instead of this
def test1(t: A[U]) = t match { case B() => ??? }

// Write this
def test2(t: A[U]) = (t: A[_]) match { case B() => ??? }

補遺: コメントの中で、質問者は「クラス レベルでは型パラメーターでは機能せず、メソッド レベルでは機能する理由についての質問は簡単です」と述べています。インスタンス化されたクラスの型パラメーターは外部から可視であり、無期限の有効期間があります。インスタンス化されたメソッドの型パラメーターについてはどちらも当てはまりません。これは、型の健全性に影響を与えます。Foo[A] が与えられた場合、Foo[Int] を取得すると、そのインスタンスを参照するとき、A は常に Int でなければなりません。

型パラメーターはまだバインドされておらず、日和見的に推論できるため、原則として、コンストラクター呼び出し内でクラス型パラメーターを同様に扱うことができます。しかし、それだけです。いったん世に出てしまえば、再交渉の余地はありません。

もう1つの補遺: コンパイラは正確さのパラゴンであり、私たちに残されたすべてのことは、私たちの理解がそれと一致するように十分に進化するまで、その出力を熟考することを前提として、これを頻繁に行っているのを目にします. そのような精神的な体操はそのテントの下で行われました.Cirque de Soleilに数回スタッフを配置することができました.

scala> case class Foo[A](f: A => A)
defined class Foo

scala> def fail(foo: Any, x: Any) = foo match { case Foo(f) => f(x) }
fail: (foo: Any, x: Any)Any

scala> fail(Foo[String](x => x), 5)
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
  at $anonfun$1.apply(<console>:15)
  at .fail(<console>:13)
  ... 33 elided

これが scala の現在のバージョンです。これは今でも機能しています。警告はありません。そのため、10 年以上存在した後では、言語と実装がそれほど健全ではないと推定することが賢明であるかどうかを自問してみてください。

于 2014-08-06T21:20:54.107 に答える
7

コンパイラの警告のようです。このことから、Martin Odersky は次のように述べています。

In the method case, what you have here is a GADT: 
Patterns determine the type parameters of an corresponding methods in the scope of a pattern case.     
GADTs are not available for class parameters.    

As far as I know, nobody has yet explored this combination, and it looks like it would be quite tricky to get this right.

PS:ここでの議論から参照を提供してくれた@retronymに感謝します


クラスの場合にエラーがスローされる理由について:

これは機能します:

sealed trait A[-K, +V]
case class B[+V]() extends A[Option[Unit], V]
case class Test[U]() {

   def test[V, X <: Unit](t: A[Option[X], V]) = t match {
     case B() => null
   }
   def test2[V](t: A[Option[U], V]) = Test2.test2(t)
}

object Test2 {
      def test2[U, V](t: A[Option[U], V]) = t match {
         case B() => null // This works
       }
 }

コンパイラがエラーをスローした理由を調べるには、次のようにしてみてください。

scala> :paste
// Entering paste mode (ctrl-D to finish)

abstract class Exp[A]{
        def eval:A = this match {
            case Temp(i) => i     //line-a
          }
        }
case class Temp[A](i:A) extends Exp[A]

// Exiting paste mode, now interpreting.

これを理解するには、§8.1.6から: 上記Tempは多相型です。ケースクラスが多態的である場合:

ケース クラスが多態的である場合、その型パラメーターはインスタンス化されるため、 c のインスタンス化はパターンの予想される型に準拠します。次に、c のプライマリ コンストラクターのインスタンス化された仮パラメーターの型が、コンポーネント パターン p 1 , . の期待される型として取得されます。. . 、pn。このパターンは、各要素パターン pi が対応する値 vi に一致するコンストラクタ呼び出し c(v1 , . . . , vn ) から作成されたすべてのオブジェクトに一致します。

つまり、コンパイラTempは行 a でスマートにインスタンス化を試み、 this( 上記で正常にコンパイルされた場合、の場合は次のTemp(1)ようExp[Int]になるため、コンパイラは行 a でパラメータとして .Temp をインスタンス化しますInt


私たちの場合、コンパイラは をインスタンス化しようとしていますB。はすでに固定されており、クラス パラメータから取得されたtA[Option[U],V]であり、ジェネリック型のメソッドであることがわかります。初期化しようとすると、最終的に. それで、どういうわけか取得しようとしています。しかし、そのままではいけません。したがって、最終的には初期化できません。これを修正すると動作しますUVBA[Option[U],V]B()A[Option[U],V]BA[Option[Unit],V]B


テスト 2 の場合は必要ありません。上記のプロセスで説明したコンパイラは、B の型パラメータを初期化しようとしています。t に型パラメータ [Option[U], V] があることを認識しています。ここで、U と V は両方とも一般的な wrt メソッドです。引数から取得されます。引数に基づいて B を初期化しようとします。引数が new B[String] の場合、B[String] を導出しようとするため、U は Option[Unit] として自動的に取得されます。引数が new A[Option[Int],String] の場合、明らかに一致しません。

于 2014-08-06T07:34:05.397 に答える
3

違いは、コンパイラとランタイムがそれぞれの場合に持っている情報と、型に対する制限の組み合わせに関係しています。以下のあいまいさは、U をトレイトとクラスのパラメーターにし、X をメソッドの型パラメーターにすることで明確になります。

sealed trait A[U]
case class B(u: Unit) extends A[Unit]

class Test[U]() {
  def test(t: A[U]) = t match {
    case B(u) => u // constructor cannot be instantiated to expected type; found : B required: A[U]
  }
}

object Test2 {
  def test2[X](t: A[X]) = t match {
    case B(x) => x // This works
  }
  def test3[X] = {
    def test(t: A[X]) = t match {
      case B(x) => x // This works
    }
  }
}

Test2.test2(new B(println("yo")))

Test2.test2 では、コンパイラは A[X] のインスタンスが提供され、X に制限がないことを認識しています。提供されたパラメーターを検査して B であるかどうかを確認し、ケースを処理するコードを生成できます。

クラス Test メソッド test では、コンパイラは、呼び出されたときに A[X] が提供されていることを認識していませんが、特定の型 U が提供されていることを認識しています。この型 U は何でもかまいません。ただし、パターン マッチは代数データ型で行われます。このデータ型には、A[Unit] 型の有効なバリエーションが 1 つだけあります。しかし、この署名は、U が限定されていない A[U] に対するものです。これは矛盾です。クラス定義では、U は自由な型パラメーターであり、メソッドではそれが Unit であると示されています。

テストをインスタンス化すると想像してください:

val t = new Test[Int]()

さて、使用場所では、方法は次のとおりです。

def test(t: A[Int])

そのような型 A[Int] はありません。B でのパターン マッチングは、コンパイラがこの条件を検査するときです。任意の U の A[U] は型と互換性がないため、「コンストラクターを期待される型にインスタンス化できません。見つかった: B が必要です: A[U]」

メソッドとクラス型パラメーターの違いは、ランタイムでメソッドが呼び出された時点で、一方はバインドされ、一方は解放されていることです。

それについて考える別の方法は、メソッドを定義することです

def test(t: A[U])

U が Unit であることを意味します。これは、U が何かであるというクラス宣言と矛盾します。

于 2014-08-06T18:32:21.740 に答える
1

編集:これは質問に対する答えではありません。参照用(およびコメント)に保管しています。


あなたが提供したリンクはすでに答えを与えています。

パラメータ化されたメソッドの場合U、実際のメソッド呼び出しの引数から推測されます。B()したがって、大文字と小文字が選択されたという事実は、U >: Unit(そうでなければメソッドが a で呼び出されなかった可能性があるB) ことを意味し、コンパイラは満足しています。

パラメータ化されたクラスの場合U、メソッドの引数から独立しています。そのため、ケースB()が選択されたという事実はコンパイラに何も伝えUず、それを確認することはできませんU >: Unit。バインドされたそのようなタイプを追加すると、機能Uするはずです。でも、試したことはありません。

于 2013-07-31T10:13:44.267 に答える