【问题标题】:infer a common supertype based on a parameter value and function parameter types根据参数值和函数参数类型推断公共超类型
【发布时间】: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 中是协变的。
  • nodeB 类型,prefix 的参数类型。
  • foldr 中函数参数f 的第二个参数与foldr 的第一个参数具有相同的类型(声明为B)。
  • 因此suffixthis 具有相同的类型,因此特别是PlayList[A]。由于B >: Asuffix.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) 类型的参数。我们正在尝试推断类型 UT 的值。
  • foldr 的第一个参数和函数的第二个参数之间存在关系 - 它们是同一个东西,T。 (部分回答丹尼尔。)
  • 我们作为这些参数传递的对象的类型是this: PlayList[A]suffix: PlayList[B]
  • 所以,由于B >: A,最具体的常见超类型是PlayList[B];因此我们有T == PlayList[B]注意,我们不需要 UT 之间的任何关系来推断这一点。

这是我卡住的地方:

  • 从编译错误信息中,推断者明确认为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


    【解决方案1】:

    我不是类型专家,但是当我尝试推断时会发生以下情况。

    ((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 选项并根据您获得的类型提示查看编译器的作用。这是 explaintypes 的输出并删除了:PlayList[B](使用 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
    

    希望这有助于阐明一些问题。可能是一个关于 scalac 何时可以推断以及何时不能推断的问题。

    【讨论】:

    • 你是对的,我正在寻找编译器来查看这些在某种意义上应该是相同的,对于这个调用,但总的来说 T 和 U 是不一样的:考虑我分别调用这两种方法的情况。该声明中的 B 只是该方法范围内使用的类型绑定的名称。在这种情况下,我的理解是 PlayList[B] 是 this 和 suffix 的常见超类型,因此应该推断为两者的类型。也就是说,prepNode 的形式类型参数 B 在本次调用中应该与 prepList 的实际类型参数 B 具有相同的值。
    • 那么可能是我的建议不正确。不过,似乎必须指定 this: PlayList[B] 对我来说是有意义的。
    【解决方案2】:

    问题是foldr没有指定B &gt;: A,所以就foldr而言,它自己的AB类型之间没有关系。就foldr 而言,suffixnode 是完全不相关的——即使您碰巧已将相关参数传递给它。

    【讨论】:

    • 我不确定我是否遵循。一般来说,我们不希望对 foldr 的声明有这种约束(当然还有其他有效用途,其中 B 与 A 无关),我认为在这种情况下我们不需要它来解析类型。我已经更新了这个问题,试图通过......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-28
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 2018-08-28
    • 2020-12-26
    相关资源
    最近更新 更多