flatMap 到底做了什么?

What does flatMap do exactly?

对于学校的编码作业,我必须用 flatmap 做一些事情,但我完全不知道它的作用,我已经在线阅读了几页并阅读了我的教科书,但我仍然没有真正理解什么确实如此。我知道 map 的作用,但出于某种原因,我很难理解 flatmap。谁能帮忙?谢谢。

只是为了添加更多信息 - 当我查看在线示例时,我确实看到平面图 returns 与地图有何不同。但是当它被调用时 flatmap 实际上在做什么?平面图实际上是如何工作的? returns 结果之前它在做什么?

打个比方。

假设您有一个大袋子,里面装满了成箱鸡蛋的购物券。如果你有一个 "use the voucher to buy a carton of eggs" 的函数并且你调用了 bigBagOfVouchers.map(buyCartonOfEggs),你就会得到一袋鸡蛋。

但是,如果您打电话给 bigBagOfVouchers.flatMap(buyCartonOfEggs),您会得到一袋鸡蛋 - 没有任何纸箱。

flatMap 将结果展平一级。 Bag[Carton[Egg]] 现在可能是 Bag[Egg]

仿函数定义具有类型

的映射
trait Functor[F[_]] {
    def map[A, B](f: A => B)(v: F[A]): F[B]
}

Monad 是支持两个附加操作的仿函数:

trait Monad[M[_]] extends Functor[M] {
    def pure[A](v: A): M[A]
    def join[A](m: M[M[A]]): M[A]
}

Join 扁平化嵌套值,例如如果 mList 那么 join 的类型是

def joinList[A](l: List[List[A]]): List[A]

如果你有一个 monad m 并且你 map 在它之上,如果 b 是相同的 monadic 类型会怎样?例如:

def replicate[A](i: Int, value: A): List[A] = ???
val f = new Functor[List] {
    def map[A, B](f: A => B)(v: List[A]) = v.map(f)
}

然后

f.map(x => replicate(x, x))(List(1,2,3)) == List(List(1), List(2,2), List(3,3,3))

它的类型为 List[List[Int]],而输入是 List[Int]。对于一系列操作来说,希望每个步骤 return 相同的输入类型是很常见的。由于 List 也可以制成 monad,因此您可以使用 join:

轻松创建这样的列表
listMonad.join(List(List(1), List(2,2), List(3,3,3))) == List(1,2,2,3,3,3)

现在您可能想编写一个函数将这两个操作合二为一:

trait Monad[M] {
   def flatMap[A, B](f: A => M[B])(m: M[A]): M[B] = join(map(f)(m))
}

那么你可以简单地做:

listMonad.flatMap(List(1,2,3), x => replicate(x, x)) == List(1,2,2,3,3,3)

flatMap 的确切作用取决于 monad 类型构造函数 M(本例中为 List),因为它取决于 mapjoin

在响应式编程中,经常会遇到需要用flatMap转换Future[Future[List]][=24=的情况] 到 Future[List]。例如,您有两个函数:get Users from database 和 process retrieved Users; return Future[List[User]]。如果将 map 应用于 getprocess,结果将是 Future[ Future[List[User]]] 这没有任何意义。相反,你应该使用 flatMap:

def main(): Future[List[User]] = getUsers flatMap processUsers    
def getUsers: Future[List[User]]
def processUsers(users: List[User]): Future[List[User]]