简体   繁体   English

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

[英]infer a common supertype based on a parameter value and function parameter types

Should the following be compiled without needing an explicit type definition on this ? 是否应编译以下内容而不需要this进行显式类型定义?

def prepList[B >: A](prefix: PlayList[B]) : PlayList[B] =
  prefix.foldr(this: PlayList[B])((node, suffix) => suffix.prepNode(node))

It seems to me that the type should be able to inferred. 在我看来,这种类型应该能够推断出来。 Is this just a limitation in the Scala compiler, or is there a type-theoretic reason that this cannot be done? 这仅仅是Scala编译器的一个限制,还是存在类型理论上无法做到这一点的原因? I don't really have a feel yet for what the Scala type inferencer can be expected to handle. 我还没有真正意识到Scala类型推理器可以处理的内容。

Working through that method: 通过该方法:

  • B >: A by definition B >: A的定义
  • this has type PlayList[A] , which is a subtype of PlayList[B] since B >: A and PlayList is covariant in A . this具有类型PlayList[A] ,其是PlayList[B]的子类型,因为B >: A和PlayList在A是协变A
  • node has type B , the parameter type of prefix . nodeB类, prefix的参数类型。
  • second parameter to function parameter f in foldr has the same type (declared B ) as the first parameter to foldr . 第二个参数为函数参数ffoldr具有相同类型(声明B )作为第一参数foldr
  • Thus suffix has the same type as this , so in particular it is a PlayList[A] . 因此suffix具有相同的类型this ,因此在特别是一个PlayList[A] Since B >: A , suffix.prepNode() takes a B . 由于B >: Asuffix.prepNode()B

I would like the compiler to see that suffix.prepNode(node) is legal where node has type B . 我希望编译器看到suffix.prepNode(node)是合法的,其中node具有类型B It appears to be able to do this only if I explicitly specify a type on the invocation of foldr or on the reference to this in that invocation. 它似乎只有在我调用foldr或在该调用的引用上显式指定类型时才能执行this

Interestingly, if I specify explicit types on the function parameters as (node: B, suffix: PlayList[B]) , a type mismatch error is still generated on the parameter to the method call suffix.prepNode(node) : "found: B, required: A" 有趣的是,如果我在函数参数上指定显式类型为(node: B, suffix: PlayList[B]) ,则仍会在方法调用suffix.prepNode(node)的参数上生成类型不匹配错误: "found: B, required: A"

I'm using Scala 2.8 RC6. 我正在使用Scala 2.8 RC6。 Full example below, the line in question is line 8. 下面的完整示例,有问题的行是第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))
}

EDIT: second attempt to reason through the compilation steps 编辑:第二次尝试通过编译步骤推理

  • Renaming for clarity, foldr takes parameters of types (T)((U, T) => T) . 为了清晰起见,重命名, foldr采用类型(T)((U, T) => T) We're trying to infer the values of types U and T . 我们试图推断出类型UT的值。
  • There is a relationship between the first parameter to foldr and the second parameter to the function - they're the same thing, T . 第一个参数到foldr和函数的第二个参数之间有一个关系 - 它们是相同的东西, T (In partial answer to Daniel.) (部分回答丹尼尔。)
  • The types of the objects we're passing as those parameters are this: PlayList[A] and suffix: PlayList[B] 我们作为参数传递的对象的类型是this: PlayList[A]suffix: PlayList[B]
  • So, since B >: A , the most specific common super type is PlayList[B] ; 因此,由于B >: A ,最具体的常见超类型是PlayList[B] ; therefore we have T == PlayList[B] . 因此我们有T == PlayList[B] Note that we don't need any relationship between U and T to deduce this. 请注意 ,我们不需要UT之间的任何关系来推断它。

This is where I get stuck: 这是我被卡住的地方:

  • From the compile error message, the inferencer clearly thinks that node has type B (that is, U == B ). 从编译错误消息中,推理器清楚地认为node具有类型B (即, U == B )。
  • I can't see how it gets to the conclusion that U == B without inferring it from the type parameter of suffix . 我无法看到如何得出U == B的结论,而不是从suffix的类型参数推断它。 (Can the scala compiler do this?) (scala编译器可以这样做吗?)
  • If that step of inference is what happens, then it follows that U == B , and we've compiled successfully. 如果推断的步骤是发生的,那么它遵循U == B ,并且我们已成功编译。 So which step went wrong? 那一步出了什么问题?

EDIT 2: In renaming the foldr parameter types above I missed that U == A by definition, it's the type parameter of the PlayList class. 编辑2:在重命名上面的foldr参数类型时,我错过了U == A的定义,它是PlayList类的类型参数。 I think this is still consistent with the above steps though, since we're calling it on an instance of PlayList[B] . 我认为这仍然与上述步骤一致,因为我们在PlayList[B]的实例上调用它。

So at the call site, T == PlayList[B] as the least common super-type of a couple of things, and U == B by definition of foldr on the receiver. 所以在呼叫站点, T == PlayList[B]作为几个事物的最不常见的超类型,并且U == B根据接收器上的foldr的定义。 That seems concise enough to narrow down to a couple of options: 这似乎足够简洁,可以缩小到几个选项:

  • the compiler can't resolve those multiple types and compute the upper bound of B 编译器无法解析那些多种类型并计算B的上限
  • bug in getting from return type PlayList[B] of foldr to type of parameter of prepNode (skeptical) 错误在从返回类型获得PlayList[B]foldr输入的参数的prepNode (怀疑)

I'm no type expert but here is what happens when I try to infer. 我不是类型专家,但是当我试图推断时会发生这种情况。

((node, suffix) => suffix.prepNode(node)) returns some unknown type PlayList[T] , where T extends A . ((node, suffix) => suffix.prepNode(node))返回一些未知类型的PlayList[T] ,其中T扩展为A. It is passed as an argument to foldr which returns the type of the function that was passed to it ( PlayList[T] where T extends A). 它作为参数传递给foldr,它返回传递给它的函数的类型( PlayList[T] ,其中T扩展A)。 And this is supposed to be of some type PlayList[B] . 这应该是某种类型的PlayList[B]

So my guess is that this:PlayList[B] is necessary to indicate that T and B are related. 所以我的猜测是this:PlayList[B]是必要的,表明T和B是相关的。

May be you need to have PlayList be parametric in two types PlayList[+A, B >: A] as you have prepNode and propList that seem to work on the same type that extends A? 可能你需要让PlayList在两个类型PlayList[+A, B >: A]参数化,因为你有prepNode和propList似乎在扩展A的相同类型上工作吗?

Said differently, your original class definition could have been defined like this: 换句话说,您的原始类定义可能已定义如下:

def prepNode[T >: A](b: T): PlayList[T]
def prepList[U >: A](prefix: PlayList[U]): PlayList[U]

But you used B in both cases and the compiler doesn't know that T and U are the same. 但是你在两种情况下都使用了B,并且编译器不知道T和U是相同的。


Edit, you can play around with the -explaintypes option and see what the compiler does depending on type hints you get. 编辑,您可以使用-explaintypes选项,并根据您获得的类型提示查看编译器的功能。 Here is the output of explaintypes and removing the :PlayList[B] (with 2.8.0.RC1): 这是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

Hope this helps shed some light. 希望这有助于解决问题。 May be a question around when scalac can infer and when it cannot infer would be helpful. 可能是一个关于scalac可以推断出来的问题以及什么时候无法推断它会有所帮助。

The problem is that foldr does not specify B >: A , so, as far as foldr is concerned, there is no relationship between it's own A and B types. 问题是foldr没有指定B >: A ,因此,就foldr而言,它自己的AB类型之间没有关系。 As far as foldr is concerned, suffix and node are completely unrelated -- even though you happen to have passed related parameters to it. foldr而言, suffixnode完全不相关 - 即使您碰巧已经将相关参数传递给它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM