flatmap如何在Scala中真正起作用

时间:2017-09-15 19:07:52

标签: scala

我参加了scala odersky课程并认为Flatmap作为参数的函数采用Monad元素并返回不同类型的monad。

trait M[T] {
    def flatMap[U](f: T => M[U]): M[U]
}

在Monad M [T]上,函数的返回类型也是相同的Monad,类型参数U可能不同。

但是我在互联网上看过一些例子,这个函数返回一个完全不同的Monad。我的印象是返回类型的函数应该是同一个Monad。有人可以简化下面的内容来解释如何在实际值而不是列表中的选项中生成。[/ p>

列表不是Scala中的Monad。

val l= List(1,2,3,4,5)
def f(x:int) = if (x>2) Some(x) else None
l.map(x=>f(x))
//Result List[Option[Int]] = List( None , None , Some(3) , Some(4) , Some(5))
l.flatMap(x=>f(x))
//Result: List(3,4,5)

3 个答案:

答案 0 :(得分:2)

让我们从M[T]本身不是单身的事实开始。它是一个类型构造函数。当它与两个运算符关联时,它会变成monad:bindreturn(或unit)。这些运营商也必须满足monad法则,但为了简洁,我们省略它们。在Haskell中,bind的类型是:

class Monad m where
  ...
  (>>=) :: m a -> (a -> m b) -> m b 

其中m是一个类型构造函数。由于Scala是OO语言,bind看起来像(第一个参数是self):

trait M[T] {
    def bind[U](f: T => M[U]): M[U]
}

此处M === mT === aU === bbind通常称为flatMap。在真空中的纯球形世界中,它将成为OO语言中flatMap的标志。 Scala是一种非常实用的语言,因此flatMap List的真正签名是:

final def flatMap[B, That](f: (A) ⇒ GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That

它不是bind,但如果您以bind的形式提供f并且确保(A) => List[B],那么它将作为一元That。 1}}是List[B]。另一方面,如果您提供不同的内容,Scala不会留意您,但会尝试找到一些有意义的转换(例如CanBuildFrom或其他内容)(如果存在)。

<强>更新

您可以使用scalac标记(-Xlog-implicits-Xlog-implicit-conversions)来查看正在发生的事情:

scala> List(1).flatMap { x => Some(x) }
<console>:1: inferred view from Some[Int] to scala.collection.GenTraversableOnce[?] via scala.this.Option.option2Iterable[Int]: (xo: Option[Int])Iterable[Int]
       List(1).flatMap { x => Some(x) }
                                  ^
res1: List[Int] = List(1)

答案 1 :(得分:1)

嗯,也许是令人困惑的,你给的签名实际上并不正确,因为它真的(简化形式):

def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): Traversable[B]

由于编译器是开源的,你实际上可以看到它正在做什么(带有完整的签名):

def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
  def builder = bf(repr) // extracted to keep method size under 35 bytes, so that it can be JIT-inlined
  val b = builder
  for (x <- this) b ++= f(x).seq
  b.result
}

因此,您可以看到实际上并不要求f的返回类型与flatMap的返回类型相同。

答案 2 :(得分:1)

标准库中的flatmap是一种比一元绑定方法更通用,更灵活的方法,如Odersky的例子中的flatMap

例如,List上flatmap的完整签名是

def flatMap[B, That](f: (A) ⇒ GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That

它不需要传递给flatmap的函数来返回List,而是能够返回任何GenTraversableOnce对象,这是一种非常通用的类型。

flatmap然后使用隐式CanBuildFrom机制来确定要返回的适当类型。

因此,当您使用带有返回List的函数的flatmap时,它是一个monadic绑定操作,但它也允许您使用其他类型。