Scala类型检查编译错误

Raf*_*aez 2 functional-programming scala compiler-errors compilation

我有以下功能

  def map[A,B](l: List[A])(f: A => B): List[B]

  def concat[A](l: List[List[A]]): List[A]
Run Code Online (Sandbox Code Playgroud)

我想实现这个

  def flatMap[A,B](l: List[A])(f: A => List[B]): List[B]
Run Code Online (Sandbox Code Playgroud)

现在,我知道正确的解决方案是(所以这不是问题)

  def flatMap[A,B](l: List[A])(f: A => List[B]): List[B] = 
    concat(map(l)(f))
Run Code Online (Sandbox Code Playgroud)

但当我试图解决它时,我首先尝试(我忘了连续)

  def flatMap[A,B](l: List[A])(f: A => List[B]): List[B] = 
    map(l)(f)

// compilation output
[error]  found   : A => fpinscala.datastructures.List[B]
[error]  required: A => B
[error]     map(l)(f)
Run Code Online (Sandbox Code Playgroud)

我无法理解那个错误,因为看起来评价map(l)(f)是错误的,但事实并非如此,它是flatMap函数的返回值有什么不对.

事实上,如果在两行代码中分解相同的实现,我们可以看到Scala编译器抱怨另一个不同的错误 - 实际上是我在前面的代码中也预期的错误.

  def flatMap[A,B](l: List[A])(f: A => List[B]): List[B] = {
    var result = map(l)(f)
    result
  }

// compilation output
[error]  found   : fpinscala.datastructures.List[fpinscala.datastructures.List[B]]
[error]  required: fpinscala.datastructures.List[B]
[error]     result
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释一下为什么第一次尝试代码的编译被赋予了第二种不同的错误?

hiv*_*ert 5

您必须知道Scala如何检查类型.它使用的是统一算法.简单来说,它意味着它遵循上/下方法.

回想一下map类型List[U] => (U => V) => List[V](无论什么类型UV是).在第一个错误的代码中,您写道您的函数返回一个List[B].因此,您告诉Scala map(l)(f)必须是类型List[B].现在你以上/下的方式传播constains.对于map(l)(f)为类型的List[B],你需要有l型的List[A](无论A是)和f类型A => B.因此编译器抱怨,因为你给f了类型A => list[B].

如果第二个错误的代码,您已经正确计算result了类型List[List[B]].但是,在第二行中,您尝试返回,result但声明您的函数返回a list[B].因此错误消息.

  • @Rafa:close - `map(l)(f)`的类型签名与`List [List [B]]`相同,但是,因为你没有指定`result`的类型,所以Scala必须[推断](https://en.wikipedia.org/wiki/Type_inference)它 - 明智地选择`List [List [B]]`,因为这是`=`操作右侧的类型.只有在尝试使用`result`作为返回值后,才会发生错误.如果你明确地写`var result:List [B] = map(l)(f)`,那么错误将出现在那一行上.这与"OOP"和"功能"几乎没有关系,它与Scala中更强大的类型推断有关. (2认同)