根据参数值和函数参数类型推断出一个共同的超类型

如果下面进行,而不需要在一个明确的类型定义编译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具有类型Bprefix的参数类型。
  • 第二个参数为函数参数ffoldr具有相同类型(声明B )作为第一参数foldr
  • 因此, suffixthis类型相同,因此特别是PlayList[A] 。 由于B >: Asuffix.prepNode()需要一个B
  • 我希望编译器能够看到,在node具有类型Bsuffix.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) 。 我们试图推断类型UT的值。
  • 第一个参数与foldr和第二个参数之间有一个关系 - 它们是相同的东西, T 。 (部分回答但以理。)
  • 我们传递的对象类型是this: PlayList[A]suffix: PlayList[B]
  • 因此,由于B >: A ,最具体的常用超类型是PlayList[B] ; 因此我们有T == PlayList[B]请注意 ,我们不需要UT之间的任何关系来推断这一点。
  • 这是我陷入困境的地方:

  • 从编译错误消息中,推理者明确地认为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而言,它自己的AB类型之间没有关系。 就foldr而言, suffixnode是完全不相关的 - 即使您碰巧已经传递了相关参数。

    链接地址: http://www.djcxy.com/p/46623.html

    上一篇: infer a common supertype based on a parameter value and function parameter types

    下一篇: Ninject with ASP.Net webforms and MVC