17

最近、(私にとっては) 奇妙なコンパイラ エラー メッセージに出くわしました。次のコードを検討してください。

trait Foo {
  type Res <: Foo
  type Bar[X <: Res]
}

class MyFoo extends Foo {
  override type Res = MyFoo
  override type Bar[X <: Res] = List[X]
}

type FOO[F <: Foo, R <: Foo, B[_ <: R]] = F { type Res = R; 
                                              type Bar[X <: R] = B[X] }

def process[F <: Foo, R <: Foo, B[_ <: R]](f: FOO[F, R, B]) {}

ここで、メソッドを呼び出したい場合はprocess、型パラメーターを明示的に記述する必要があります。

process[MyFoo, MyFoo, List](new MyFoo) // fine

私が書く場合:

process(new MyFoo)

また

process((new MyFoo): FOO[MyFoo, MyFoo, List])

次のエラー メッセージが表示されます。

型引数の推定された種類 (MyFoo、MyFoo、List[X]) は、型パラメーターの予想される種類 (型 F、型 R、型 B) に準拠していません。List[X] の型パラメーターは、型 B の予期されるパラメーターと一致しません: クラス List には型パラメーターが 1 つありますが、型 B には型パラメーターが 1 つあります

コンパイラが型を推論できないのはなぜですか (呼び出しパラメータで明示的に述べたにもかかわらず)? そして、それはclass List has one type parameter, but type B has oneどういう意味ですか?どちらかが 1 つを持っているが、もう1 つも1 つを持っている。

4

2 に答える 2

3

Scala コンパイラに目を向けると、ソースが問題の原因を理解するのに役立つ可能性があります。私は Scala コンパイラーに貢献したことはありませんが、ソースは非常に読みやすく、それについては既に調査済みです。

型推論を担当するクラスscala.tools.nsctypechecker.Inferは、エラーの一部について Scala コンパイラ ソースを調べるだけで見つけることができます。次のフラグメントが見つかります。

  /** error if arguments not within bounds. */
    def checkBounds(pos: Position, pre: Type, owner: Symbol,
                    tparams: List[Symbol], targs: List[Type], prefix: String) = {
      //@M validate variances & bounds of targs wrt variances & bounds of tparams
      //@M TODO: better place to check this?
      //@M TODO: errors for getters & setters are reported separately
      val kindErrors = checkKindBounds(tparams, targs, pre, owner)

      if(!kindErrors.isEmpty) {
        error(pos,
          prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") +
          " do not conform to the expected kinds of the type parameters "+ tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." +
          kindErrors.toList.mkString("\n", ", ", ""))
      } 

ここで重要なのは、checkKindBounds(tparams, targs, pre, owner)これらのエラーが返される理由を理解することです。メソッド呼び出しチェーンをたどると、checkKindBounds が別のメソッドを呼び出していることがわかります。

val errors = checkKindBounds0(tparams, targs, pre, owner, true)

checkKindBoundsHK 内の 5784 行目で、問題が高次型の境界のチェックに関連していることがわかります。

 if (!sameLength(hkargs, hkparams)) {
        if (arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded
        else {error = true; (List((arg, param)), Nil, Nil) } // shortcut: always set error, whether explainTypesOrNot
      }

テストに合格しませんでした。デバッガーでは次のように表示されます。

hkargs$1 = {scala.collection.immutable.Nil$@2541}"List()"
arg$1 = {scala.tools.nsc.symtab.Symbols$ClassSymbol@2689}"class List"
param$1 = {scala.tools.nsc.symtab.Symbols$TypeSymbol@2557}"type B"
paramowner$1 = {scala.tools.nsc.symtab.Symbols$MethodSymbol@2692}"method process"
underHKParams$1 = {scala.collection.immutable.$colon$colon@2688}"List(type R)"
withHKArgs$1 = {scala.collection.immutable.Nil$@2541}"List()"
exceptionResult12 = null
hkparams$1 = {scala.collection.immutable.$colon$colon@2688}"List(type R)"

そのため、タイプ R という上位の種類のパラメーターが 1 つあるように見えますが、その値は提供されていません。

実際に checkKindBounds に戻ると、スニペットの後に次のことがわかります。

 val (arityMismatches, varianceMismatches, stricterBounds) = (
        // NOTE: *not* targ.typeSymbol, which normalizes
        checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO)
      )

arityMismatchesはタプル リスト B が含まれています。また、エラー メッセージが間違っていることもわかります。

型引数の推定された種類 (MyFoo、MyFoo、List[X]) は、型パラメーターの予想される種類 (型 F、型 R、型 B) に準拠していません。List[X] の型パラメーターは、型 B の予期されるパラメーターと一致しません: クラス List には 1 つの型パラメーターがありますが、型 B には0があります

実際、次の呼び出しで 5859 行目にブレークポイントを設定すると、

checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO)

あなたはそれを見ることができます

tparam = {scala.tools.nsc.symtab.Symbols$TypeSymbol@2472}"type B"
targ = {scala.tools.nsc.symtab.Types$UniqueTypeRef@2473}"List[X]"

結論:

何らかの理由で、あなたのような複雑な高カインド型を扱う場合、Scala コンパイラーの推論は制限されます。それがどこから来たのかはわかりません。バグをコンパイラ チームに送りたいと思うかもしれません。

于 2012-10-04T13:01:42.810 に答える
0

私は Scala での型推論の正確な仕組みについて漠然としか理解していないので、このアイデアは決定的な答えではないと考えてください。

  1. 型推論には、一度に複数の型を推論するという問題があります。

  2. FOO の定義で存在型を使用すると、次のように変換されます。

于 2012-10-04T08:37:17.957 に答える