根据参数值和函数参数类型推断出一个共同的超类型
如果下面进行,而不需要在一个明确的类型定义编译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