为什么Scala在嵌套类型参数时不能完全推断类型参数?

时间:2011-07-31 22:34:12

标签: scala

考虑以下Scala代码:

abstract class A
abstract class B[T <: A]
class ConcreteA extends A
class ConcreteB extends B[ConcreteA]

class Example[U <: B[T], T <: A]( resolver: U )
object Test {
    new Example( new ConcreteB )
}

最后一行new Example( new ConcreteB )无法编译,并出现以下错误:

  

错误:推断类型参数[ConcreteB,Nothing]不符合类Example的类型参数bounds [U&lt;:B [T],T&lt;:A]

但是ConcreteB拥有解决U T的所有必要数据。我在这里缺少什么?

4 个答案:

答案 0 :(得分:10)

(另请参阅两个相关问题:Scala fails to infer the right type argumentsType infered to Nothing in Scala

它看起来像Scala的类型推断的限制,有意没有规定。作为解决方法,您可以通过T B的{​​{1}}类型成员而非参数

来推断。
abstract class A
abstract class B { type T <: A }
class ConcreteA extends A
class ConcreteB extends B { type T = ConcreteA }
class Example[U <: B]( resolver: U )
object Test {
    new Example( new ConcreteB )
}

当使用类型成员时,知道它们可以使用细化作为类型参数浮出来是有用的,如Miles Sabin对:Why is this cyclic reference with a type projection illegal?的回答

在Jean-Philippe Pellet对a related question的回答中,通过使类型参数更高的类型来辅助类型推断。如果ConcreteB中引入了额外的类型参数,那么类型推断就可以了,

abstract class A
abstract class B[T <: A]
class ConcreteA extends A
class ConcreteB[T <: A] extends B[T]
class Example[T <: A, U[T0 <: A] <: B[T0]]( resolver: U[T] )
object Test {
  new Example( new ConcreteB[ConcreteA] )
}

Scala 2.9给出了下面的神秘错误消息,但Miles Sabin指出这是一个将修复为2.9.1的错误

<console>:15: error: kinds of the type arguments (ConcreteA,ConcreteB[T0]) do not conform to the expected kinds of the type parameters (type T,type U) in class Example.
ConcreteB[T0]'s type parameters do not match type U's expected parameters: class ConcreteB has one type parameter, but type U has one
         new Example( new ConcreteB[ConcreteA] )
             ^

答案 1 :(得分:10)

凯普顿与他的高级解决方案接近了。不幸的是,他绊倒了似乎是Scala中的一个错误&lt; 2.9.1.RC1。以下工作与2.9.1.RC1和trunk,

一样正常
Welcome to Scala version 2.9.1.RC1 (Java HotSpot(TM) Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.

scala> abstract class A
defined class A

scala> abstract class B[T <: A]
defined class B

scala> class ConcreteA extends A
defined class ConcreteA

scala> class ConcreteB[T <: A] extends B[T]
defined class ConcreteB

scala> class Example[T <: A, U[X <: A] <: B[X]](resolver: U[T])
defined class Example

scala> new Example(new ConcreteB[ConcreteA])
res0: Example[ConcreteA,ConcreteB] = Example@ec48e7

答案 2 :(得分:2)

我已经在GitHub上编写了a document of type inference workarounds以供我自己学习。

我觉得有用的一些简单规则是:

  • 无法推断类型参数的类型参数: Scala类型推断只能看到参数列表中指定的类型(不要与类型混淆参数列表)。

  • 以前的参数不会用于推断未来的参数:类型信息仅流过参数列表,而不是参数


然而,在这个特定的例子中,类型成员是前进的方向(感谢@Kipton Barros!)

答案 3 :(得分:1)

works 在 Scala 3 中改进了推理,因此不再需要上述解决方法。例如,类型参数不必总是出现在(值)参数列表中以进行推断,因此我们可以编写

def f[F <: List[A], A](as: F)

代替

def f[F <: List[A], A](as: F[A])

例如

➜  ~ scala3-repl -version
Scala code runner version 3.0.0-RC2 -- Copyright 2002-2021, LAMP/EPFL
➜  ~ scala3-repl         
scala> def f[F <: List[A], A](as: F) = as                                                                                                                
def f[F <: List[A], A](as: F): F

scala> f(List(42))                                                                                                                                       
val res0: List[Int] = List(42)

我们看到的 F 被推断为 ListA 被推断为 Int