Joe*_*ney 7 scala type-inference scala-2.8
是否应编译以下内容而不需要明确的类型定义this?
def prepList[B >: A](prefix: PlayList[B]) : PlayList[B] =
prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))
Run Code Online (Sandbox Code Playgroud)
在我看来,这种类型应该能够推断出来.这仅仅是Scala编译器的一个限制,还是存在类型理论上无法做到这一点的原因?我还没有真正意识到Scala类型推理器可以处理的内容.
通过该方法:
B >: A 根据定义this具有类型PlayList[A],这是的一个子类型PlayList[B],因为B >: A和播放列表在协变A.node有类型B,参数类型prefix.fin的第二个参数与第一个参数的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))
}
Run Code Online (Sandbox Code Playgroud)
编辑:第二次尝试通过编译步骤推理
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接收器的定义.这似乎足够简洁,可以缩小到几个选项:
BPlayList[B]的错误foldrprepNode我不是类型专家,但当我尝试推断时会发生以下情况。
((node, suffix) => suffix.prepNode(node))返回某种未知类型PlayList[T],其中 T 扩展 A 。它作为参数传递给foldr,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]
Run Code Online (Sandbox Code Playgroud)
但是您在两种情况下都使用了 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
Run Code Online (Sandbox Code Playgroud)
希望这有助于阐明一些道理。可能是关于 scalac 何时可以推断以及何时不能推断的问题会有所帮助。
| 归档时间: |
|
| 查看次数: |
744 次 |
| 最近记录: |