为什么我必须在fold中写下匿名函数的返回类型

时间:2015-10-18 15:33:14

标签: scala functional-programming type-inference

在Scala中给出此代码:

package fpinscala.datastructures
sealed trait Tree[+A]
case class Leaf[A](value: A) extends Tree[A]
case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]

object Tree {

  def fold[A,B](t: Tree[A])(f: A => B)(g: (B,B) => B): B = {
    t match {
      case Leaf(x) => f(x)
      case Branch(l,r) => g(fold(l)(f)(g),fold(r)(f)(g))
    }
  }

  def mapViaFold[A,B](t:Tree[A])(f: A => B): Tree[B] = {
    fold(t)(a => Leaf(f(a)): Tree[B])(Branch(_,_))
  }

}

为什么我要写下函数a => Leaf(f(a)): Tree[B]的返回类型?没有它,我收到错误消息:

Error:(54, 36) type mismatch;
 found   : fpinscala.datastructures.Branch[B]
 required: fpinscala.datastructures.Leaf[B]
    fold(t)(a => Leaf(f(a)))(Branch(_,_))
                                   ^

1 个答案:

答案 0 :(得分:1)

因为折叠定义中的类型参数B在这种情况下从术语Leaf[B]推断为Leaf(f(a))。然后,您的类型与Branch(_,_)(Leaf[B],Leaf[B]) => Branch[B]类型不匹配,永远不会是所需类型(Leaf[B], Leaf[B]) => Leaf[B]的子类型。

<强>更新 我认为这种行为是由于Scala中的类型重建是如何工作的:它基于参数列表(不是单个参数),而是从左到右。因此,一旦编译器能够推断出参数列表的某些类型,它将使用它们来解析右侧的参数列表的类型,而不是相反。

下面我试图以更简单的形式提取问题:

trait A
class B extends A
class C extends A

def f[T](x: T)(y: T): Int = 1
def g[T](x: T, y: T): Int = 1

f(new B)(new C) // Does not compile
g(new B, new C) // Compiles

在咖喱版本中,T在第一个参数列表后被推断为B,然后我们在第二个参数列表中得到一个类型错误。

在不紧不慢的版本中,编译器使用唯一参数列表中的所有参数来推断T的类型,在这种情况下,正如我们预期的那样是最小上限A.