在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(_,_))
^
答案 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.