如果编译以下内容而不需要this
上的显式类型定义吗?
def prepList[B >: A](prefix: PlayList[B]) : PlayList[B] =
prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))
在我看来,这种类型应该能够推断出来。这仅仅是Scala编译器的一个限制,还是存在类型理论上无法做到这一点的原因?我还没有真正意识到Scala类型推理器可以处理的内容。
完成该方法:
B >: A
this
的类型为PlayList[A]
,这是PlayList[B]
的子类型,因为B >: A
和PlayList在A
中是协变的。node
的类型为B
,参数类型为prefix
。f
中函数参数foldr
的第二个参数与B
的第一个参数具有相同的类型(声明为foldr
)。suffix
与this
具有相同的类型,因此特别是PlayList[A]
。由于B >: A
,suffix.prepNode()
需要B
。我希望编译器看到suffix.prepNode(node)
在node
类型为B
的情况下是合法的。只有在foldr
的调用或在该调用中对this
的引用上显式指定类型时,它似乎才能执行此操作。
有趣的是,如果我将函数参数的显式类型指定为(node: B, suffix: PlayList[B])
,则仍会在方法调用suffix.prepNode(node)
的参数上生成类型不匹配错误:"found: B, required: A"
我正在使用Scala 2.8 RC6。下面的完整示例中,有问题的行是第8行。
sealed abstract class PlayList[+A] {
import PlayList._
def foldr[B](b: B)(f: (A, B) => B): B
def prepNode[B >: A](b: B): PlayList[B] = nel(b, this)
def prepList[B >: A](prefix: PlayList[B]): PlayList[B] =
// need to specify type here explicitly
prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))
override def toString = foldr("")((node, string) => node + "::" + string)
}
object PlayList {
def nil[A]: PlayList[A] = Nil
def nel[A](head: A, tail: PlayList[A]): PlayList[A] = Nel(head, tail)
def nel[A](as: A*): PlayList[A] = as.foldRight(nil[A])((a, l) => l.prepNode(a))
}
case object Nil extends PlayList[Nothing] {
def foldr[B](b: B)(f: (Nothing, B) => B) = b
}
case class Nel[+A](head: A, tail: PlayList[A]) extends PlayList[A] {
def foldr[B](b: B)(f: (A, B) => B) = f(head, tail.foldr(b)(f))
}
编辑:第二次尝试通过编译步骤进行推理
foldr
采用类型(T)((U, T) => T)
的参数。我们正在尝试推断类型U
和T
的值。foldr
与函数的第二个参数之间存在关系 - 它们是相同的,T
。 (部分回答丹尼尔。)this: PlayList[A]
和suffix: PlayList[B]
B >: A
以来,最具体的常见超类型是PlayList[B]
;因此我们有T == PlayList[B]
。 注意我们不需要U
和T
之间的任何关系来推断这一点。这是我被困的地方:
node
具有类型B
(即U == B
)。U == B
如何在不suffix
的类型参数中推断它的结论。 (scala编译器可以这样做吗?)U == B
,我们已成功编译。那一步出错了? 编辑2:在重命名上面的foldr
参数类型时,我错过了U == A
的定义,它是PlayList
类的类型参数。我认为这仍然与上述步骤一致,因为我们在PlayList[B]
的实例上调用它。
因此,在呼叫网站上,T == PlayList[B]
是最不常见的超级类型,而U == B
根据接收者的foldr
定义。这似乎足够简洁,可以缩小到几个选项:
B
PlayList[B]
的返回类型foldr
获取prepNode
的参数类型(持怀疑态度)答案 0 :(得分:3)
我不是类型专家,但是当我试图推断时会发生这种情况。
((node, suffix) => suffix.prepNode(node))
返回一些未知类型PlayList[T]
,其中T扩展为A.它作为参数传递给foldr,它返回传递给它的函数的类型(PlayList[T]
,其中T扩展A)。这应该是某种类型PlayList[B]
。
所以我的猜测是this:PlayList[B]
是必要的,表明T和B是相关的。
您可能需要让PlayList在两种类型PlayList[+A, B >: A]
中参数化,因为您的prepNode和propList似乎与扩展A的相同类型有效吗?
换句话说,您的原始类定义可以像这样定义:
def prepNode[T >: A](b: T): PlayList[T]
def prepList[U >: A](prefix: PlayList[U]): PlayList[U]
但是你在两种情况下都使用了B,并且编译器不知道T和U是相同的。
编辑,您可以使用-explaintypes选项,并根据您获得的类型提示查看编译器的功能。这是explaintypes的输出并删除:PlayList[B]
(使用2.8.0.RC1):
$ scalac -explaintypes -d classes Infer.scala
found : node.type (with underlying type B)
required: A
prefix.foldr(this)((node, suffix) => suffix.prepNode(node))
^
node.type <: A?
node.type <: Nothing?
B <: Nothing?
<notype> <: Nothing?
false
Any <: Nothing?
<notype> <: Nothing?
false
false
false
false
B <: A?
B <: Nothing?
<notype> <: Nothing?
false
Any <: Nothing?
<notype> <: Nothing?
false
false
false
Any <: A?
Any <: Nothing?
<notype> <: Nothing?
false
false
false
false
false
希望这有助于解决问题。可能是一个关于scalac可以推断出来的问题以及什么时候无法推断它会有所帮助。
答案 1 :(得分:1)
问题是foldr
没有指定B >: A
,因此,就foldr
而言,它自己的A
和{{1}之间没有任何关系类型。就B
而言,foldr
和suffix
完全无关 - 即使您碰巧已将相关参数传递给它。