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()
    接受A
    B
我希望编译器看到
后缀.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
    • 如果不从后缀
      的类型参数推断,我看不出它是如何得出
      U==B
      的结论的。(scala编译器能做到这一点吗?)
    • 如果推理的步骤是这样的,那么接下来就是
      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