Scala 基于参数值和函数参数类型推断通用超类型
是否应编译以下内容,而不需要对此Scala 基于参数值和函数参数类型推断通用超类型,scala,scala-2.8,type-inference,Scala,Scala 2.8,Type Inference,是否应编译以下内容,而不需要对此进行显式类型定义 def prepList[B >: A](prefix: PlayList[B]) : PlayList[B] = prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node)) 在我看来,该类型应该能够推断。这仅仅是Scala编译器中的一个限制,还是有一个类型理论上的原因导致不能这样做?我还不知道Scala类型的推理器可以处理什么 通过该方法开展
进行显式类型定义
def prepList[B >: A](prefix: PlayList[B]) : PlayList[B] =
prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))
在我看来,该类型应该能够推断。这仅仅是Scala编译器中的一个限制,还是有一个类型理论上的原因导致不能这样做?我还不知道Scala类型的推理器可以处理什么
通过该方法开展工作:
B>:定义为A
此
具有类型播放列表[A]
,它是播放列表[B]
的子类型,因为B>:A
和播放列表在A
中是协变的
节点
的类型为B
,参数类型为前缀
foldr
中函数参数f
的第二个参数与foldr
的第一个参数具有相同的类型(声明为B
)
- 因此
后缀
与此
具有相同的类型,因此它尤其是播放列表[a]
。由于B>:A
,后缀.prepNode()
接受AB
我希望编译器看到后缀.prepNode(node)
合法,其中节点
的类型为B
。只有在调用foldr
或在该调用中对this
的引用上明确指定一个类型时,它才能做到这一点
有趣的是,如果我在函数参数上指定显式类型为(节点:B,后缀:PlayList[B])
,那么在方法调用后缀的参数上仍然会生成类型不匹配错误。prepNode(节点)
:“found:B,required:a”
我使用的是Scala2.8RC6。下面的完整示例中,所讨论的行是第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
。(部分回答丹尼尔。)
- 我们作为这些参数传递的对象的类型是:PlayList[A]
和后缀:PlayList[B]
B>:A
,最常见的超级类型是播放列表[B]
;因此我们有T==PlayList[B]
注意我们不需要U
和t
之间的任何关系来推断这一点- 从编译错误消息中,推断器清楚地认为
具有类型节点
(即,B
)U==B
- 如果不从后缀
的类型参数推断,我看不出它是如何得出
的结论的。(scala编译器能做到这一点吗?)U==B
- 如果推理的步骤是这样的,那么接下来就是
,我们已经成功编译了。那么哪一步出错了U==B
编辑2:在重命名上面的
foldr
参数类型时,我遗漏了U==A
根据定义,它是播放列表
类的类型参数。我认为这仍然与上面的步骤一致,因为我们在PlayList[B]
的实例上调用它
因此,在呼叫站点,T==PlayList[B]
是最不常见的超级类型,而根据接收器上的foldr
的定义,U==B
。这似乎足够简洁,可以缩小为几个选项:
- 编译器无法解析这些多类型并计算
B
- 从
的返回类型foldr
到PlayList[B]
的参数类型中获取错误(怀疑)prepNode
((node,suffix)=>suffix.prepNode(node))
返回一些未知类型的播放列表[T]
,其中T扩展了。它作为参数传递给foldr,foldr返回传递给它的函数类型(PlayList[T]
其中T扩展了A)。这应该是某种类型的播放列表[B]
所以我猜,播放列表[B]是必要的,以表明T和B是相关的
可能您需要将播放列表参数化为两种类型播放列表[+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选项,并根据得到的类型提示查看编译器执行的操作。以下是解释类型的输出和删除
:播放列表[B]
(使用2.8.0.RC1):
$scalac-explaintypes-d类推断.scala
找到:node.type(具有基础类型B)
所需:A
prefix.foldr(this)((节点,后缀)=>后缀.prepNode(节点))
^
node.type问题在于foldr
没有指定B>:A
,因此,就foldr
而言,它自己的A
和B
类型之间没有关系。就foldr
而言,suffix
和node
是完全不相关的——即使您碰巧向它传递了相关的参数。您是对的,我正在寻找编译器,以确保它们在某种意义上应该是相同的
$ 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