类 A 有一个类型参数,但类型 B 有一个 [英] class A has one type parameter, but type B has one

查看:50
本文介绍了类 A 有一个类型参数,但类型 B 有一个的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我偶然发现了一条奇怪的(对我来说)编译器错误消息.考虑以下代码:

Recently I stumbled across a strange (to me) compiler error message. Consider the following code:

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 方法,我必须明确地编写类型参数:

Now, if I want to call the process method I have to explicitly write the type parameters:

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 有一个类型参数,但类型 B 有一个

inferred kinds of the type arguments (MyFoo,MyFoo,List[X]) do not conform to the expected kinds of the type parameters (type F,type R,type B). List[X]'s type parameters do not match type B's expected parameters: class List has one type parameter, but type B has one

为什么编译器不能推断类型(尽管我在调用参数中明确说明了它们)?那 class List 有一个类型参数,而类型 B 有一个是什么意思?有些东西有一个,但另一个有一个,这就是为什么它们不适合在一起???

Why isn´t the compiler able to infer the types (although I explicitly stated them at call parameter)? And what does that class List has one type parameter, but type B has one mean? Something has one, but the other has also one, and that´s why they don´t fit together???

推荐答案

如果我们查看 Scala 编译器,源代码可以帮助我们了解问题所在.我从未对 Scala 编译器做出过贡献,但我发现源代码非常易读,我已经对此进行了调查.

If we look to the Scala compiler, the sources could help us understanding what the problem is. I have never contributed to the Scala compiler, but I found the sources very readable and I have already investigated on that.

负责类型推断的类是 scala.tools.nsctypechecker.Infer,您只需在 Scala 编译器源代码中查找部分错误即可找到它.您会发现以下片段:

The class responsible for type inference is scala.tools.nsctypechecker.Infer which you can find simply by looking in the Scala compiler sources for a part of your error. You'll find out the following fragment:

  /** 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 调用了另一个方法

So now the point is understanding why checkKindBounds(tparams, targs, pre, owner) returns those errors. If you go down the method call chain, you will see that the checkKindBounds call another method

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

您会看到问题与检查更高级类型的边界有关,在第 5784 行,在 checkKindBoundsHK 中:

You'll see the problem is connected to checking bounds of higher-kinded type, at line 5784, inside checkKindBoundsHK :

 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
      }

测试没有通过,看来在我的调试器中:

The test is not passed, it appears that in my debugger:

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,但没有提供它的值.

So it appears like there is one higher kinded param, type R, but there is no provided value for that.

如果你真的回到 checkKindBounds,你会在代码片段之后看到:

If you actually go back to the to checkKindBounds, you see that after the snippet:

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

arityMismatches 包含一个元组列表,B.现在你也可以看到错误信息是错误的:

the arityMismatches contains a tuple List, B. And now you can also see that the error message is wrong:

类型参数的推断类型 (MyFoo,MyFoo,List[X]) 不符合类型参数的预期类型(类型 F,类型R,B 型).List[X] 的类型参数与类型 B 的预期不匹配参数:类 List 有一个类型参数,但类型 B 有 ZERO

inferred kinds of the type arguments (MyFoo,MyFoo,List[X]) do not conform to the expected kinds of the type parameters (type F,type R,type B). List[X]'s type parameters do not match type B's expected parameters: class List has one type parameter, but type B has ZERO

事实上,如果您在以下调用的第 5859 行设置断点

In fact if you put a breakpoint at line 5859 on the following call

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 编译器推断是有限的.我不知道它来自哪里,也许你想向编译器团队发送一个错误

For some reason, when dealing with complex higher-kinded types such as yours, Scala compiler inference is limited. I don't know where it does come from, maybe you want to send a bug to the compiler team

这篇关于类 A 有一个类型参数,但类型 B 有一个的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆