【发布时间】:2010-06-28 10:38:46
【问题描述】:
是否应该在不需要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的参数类型。 -
foldr中函数参数f的第二个参数与foldr的第一个参数具有相同的类型(声明为B)。 - 因此
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)。 - 如果不从
suffix的类型参数推断,我看不出它是如何得出U == B的结论的。 (scala 编译器可以这样做吗?) - 如果推理步骤是这样发生的,那么它遵循
U == B,我们已经编译成功。那么哪一步出了问题?
编辑 2: 在重命名上面的 foldr 参数类型时,我错过了 U == A 的定义,它是 PlayList 类的类型参数。我认为这仍然与上述步骤一致,因为我们在 PlayList[B] 的实例上调用它。
因此,在呼叫站点,T == PlayList[B] 是最不常见的超类型,而U == B 在接收器上定义为foldr。这似乎足够简洁,可以缩小到几个选项:
- 编译器无法解析这些多种类型并计算
B的上限 - 从
foldr的返回类型PlayList[B]获取prepNode的参数类型时出现错误(怀疑)
【问题讨论】:
标签: scala scala-2.8 type-inference