Monads - Flatten 的目的
Monads - Purpose of Flatten
所以我一直在阅读有关 scala 中的 Monads 的文章,以及与它们的 flatMap
函数和 for
理解相关的所有语法。直觉上,我理解为什么 Monads 需要使用 flatMap
函数的 map
部分,就像我通常在零、一个或多个元素的容器上使用 map
函数和 return是同一个容器,但传入的函数应用于容器的所有元素。 Monad 同样是零个、一个或多个元素的容器。
但是,flatMap
的 flatten
部分的目的是什么?我无法理解它背后的直觉。对我来说,这似乎是额外的样板,需要传递给 flatMap
的所有函数来围绕它们的 return 值创建一个容器/monad,只是让该容器立即被 flatten
部分销毁flatMap
。我想不出一个使用 flatMap
的例子,它不能通过简单地替换为 map
来简化。例如:
var monad = Option(5)
var monad2 = None
def flatAdder(i:Int) = Option(i + 1)
def adder(i:Int) = i + 1
// What I see in all the examples
monad.flatMap(flatAdder)
// Option[Int] = Some(6)
monad.flatMap(flatAdder).flatMap(flatAdder)
// Option[Int] = Some(7)
monad2.flatMap(flatAdder)
// Option[Int] = None
monad2.flatMap(flatAdder).flatMap(flatAdder)
// Option[Int] = None
// Isn't this a lot easier?
monad.map(adder)
// Option[Int] = Some(6)
monad.map(adder).map(adder)
// Option[Int] = Some(7)
monad2.map(adder)
// Option[Int] = None
monad2.map(adder).map(adder)
// Option[Int] = None
对我来说,单独使用 map
似乎比 flatMap
更直观和简单,而且 flatten
部分似乎没有增加任何价值。但是,在 Scala 中,重点放在了 flatMap
而不是 map
上,以至于它甚至获得了自己的 for
理解语法,所以很明显我一定遗漏了一些东西。我的问题是:在什么情况下 flatMap
的 flatten
部分实际上有用? flatMap
比 map
有哪些其他优势?
如果处理的每个元素都可能产生零个或多个元素怎么办?
List(4, 0, 15).flatMap(n => primeFactors(n))
//List(2, 2, 3, 5)
如果你有一个名字,但可能拼写不正确,并且你想要他们的办公室分配,如果他们有的话怎么办?
def getID(name:String): Option[EmployeeID] = ???
def getOffice(id:EmployeeID): Option[Office] = ???
val office :Option[Office] =
getID(nameAttempt).flatMap(id => getOffice(id))
当你 有 一个 monad 并且你需要将其内容提供给 monad 生产者时,你使用 flatMap()
。您想要结果 Monad[X]
而不是 Monad[Monad[X]]
.
flatMap
背后的想法是能够获取 M[A]
的值和类型为 A => M[B]
的函数 f
并生成 M[B]
,如果我们没有办法获得 "flatten"
的值,那么我们最终总是会得到一个 M[M[B]]
,这是我们大多数时候不想要的。
(是的,有些情况下您可能需要一个 M[M[_]]
,例如当您想要创建 M[_]
的多个实例时)
所以我一直在阅读有关 scala 中的 Monads 的文章,以及与它们的 flatMap
函数和 for
理解相关的所有语法。直觉上,我理解为什么 Monads 需要使用 flatMap
函数的 map
部分,就像我通常在零、一个或多个元素的容器上使用 map
函数和 return是同一个容器,但传入的函数应用于容器的所有元素。 Monad 同样是零个、一个或多个元素的容器。
但是,flatMap
的 flatten
部分的目的是什么?我无法理解它背后的直觉。对我来说,这似乎是额外的样板,需要传递给 flatMap
的所有函数来围绕它们的 return 值创建一个容器/monad,只是让该容器立即被 flatten
部分销毁flatMap
。我想不出一个使用 flatMap
的例子,它不能通过简单地替换为 map
来简化。例如:
var monad = Option(5)
var monad2 = None
def flatAdder(i:Int) = Option(i + 1)
def adder(i:Int) = i + 1
// What I see in all the examples
monad.flatMap(flatAdder)
// Option[Int] = Some(6)
monad.flatMap(flatAdder).flatMap(flatAdder)
// Option[Int] = Some(7)
monad2.flatMap(flatAdder)
// Option[Int] = None
monad2.flatMap(flatAdder).flatMap(flatAdder)
// Option[Int] = None
// Isn't this a lot easier?
monad.map(adder)
// Option[Int] = Some(6)
monad.map(adder).map(adder)
// Option[Int] = Some(7)
monad2.map(adder)
// Option[Int] = None
monad2.map(adder).map(adder)
// Option[Int] = None
对我来说,单独使用 map
似乎比 flatMap
更直观和简单,而且 flatten
部分似乎没有增加任何价值。但是,在 Scala 中,重点放在了 flatMap
而不是 map
上,以至于它甚至获得了自己的 for
理解语法,所以很明显我一定遗漏了一些东西。我的问题是:在什么情况下 flatMap
的 flatten
部分实际上有用? flatMap
比 map
有哪些其他优势?
如果处理的每个元素都可能产生零个或多个元素怎么办?
List(4, 0, 15).flatMap(n => primeFactors(n))
//List(2, 2, 3, 5)
如果你有一个名字,但可能拼写不正确,并且你想要他们的办公室分配,如果他们有的话怎么办?
def getID(name:String): Option[EmployeeID] = ???
def getOffice(id:EmployeeID): Option[Office] = ???
val office :Option[Office] =
getID(nameAttempt).flatMap(id => getOffice(id))
当你 有 一个 monad 并且你需要将其内容提供给 monad 生产者时,你使用 flatMap()
。您想要结果 Monad[X]
而不是 Monad[Monad[X]]
.
flatMap
背后的想法是能够获取 M[A]
的值和类型为 A => M[B]
的函数 f
并生成 M[B]
,如果我们没有办法获得 "flatten"
的值,那么我们最终总是会得到一个 M[M[B]]
,这是我们大多数时候不想要的。
(是的,有些情况下您可能需要一个 M[M[_]]
,例如当您想要创建 M[_]
的多个实例时)