根据参数值和函数参数类型推断出一个共同的超类型
如果下面进行,而不需要在一个明确的类型定义编译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是协变A 。 node具有类型B , prefix的参数类型。 f在foldr具有相同类型(声明B )作为第一参数foldr 。 suffix与this类型相同,因此特别是PlayList[A] 。 由于B >: A , suffix.prepNode()需要一个B 我希望编译器能够看到,在node具有类型B , suffix.prepNode(node)是合法的。 只有当我在调用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 (怀疑) 我不是类型专家,但是当我试图推断时,会发生什么。
((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选项,并根据你得到的类型提示,看看编译器做了什么。 这里是解释类型的输出并删除:PlayList[B] (with 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
希望这有助于解决一些问题。 可能是什么时候什么时候可以推断,什么时候推断不会有帮助。
问题是foldr没有指定B >: A ,所以就foldr而言,它自己的A和B类型之间没有关系。 就foldr而言, suffix和node是完全不相关的 - 即使您碰巧已经传递了相关参数。
上一篇: infer a common supertype based on a parameter value and function parameter types
