我一直在研究FP语言(关闭和开启)一段时间,并且使用过Scala,Haskell,F#和其他一些语言.我喜欢我所看到的并理解FP的一些基本概念(绝对没有类别理论的背景 - 所以请不要谈数学,请).
所以,给定一个类型,M[A]
我们有map
一个函数,A=>B
并返回一个M[B]
.但我们也有flatMap
一个功能A=>M[B]
并返回一个M[B]
.我们也有flatten
一个M[M[A]]
并且返回一个M[A]
.
此外,许多的来源我已阅读形容flatMap
为map
其次flatten
.
所以,鉴于这flatMap
似乎相当于flatten compose map
,它的目的是什么?请不要说它是支持'for comprehensions',因为这个问题确实不是Scala特有的.我不太关心语法糖,而不是我背后的概念.Haskell的bind运算符(>>=
)也出现了同样的问题.我相信它们都与某些类别理论概念有关,但我不会说那种语言.
我已经看过Brian Beckman的精彩视频Do not Fear the Monad不止一次,我想我看到那flatMap
是monadic组合运算符,但我从未真正看到它使用了他描述这个运算符的方式.它执行此功能吗?如果是这样,我该如何将该概念映射到flatMap
?
顺便说一句,我在这个问题上写了很长的文章,上面有很多列表显示我试图深入了解其中的实验flatMap
,然后遇到了这个回答我的一些问题的问题.有时我讨厌Scala暗示.他们真的可以在水中浑浊.:)
FlatMap,在其他一些语言中被称为"绑定",就像你自己为功能组合所说的那样.
想象一下,你有一些像这样的功能:
def foo(x: Int): Option[Int] = Some(x + 2) def bar(x: Int): Option[Int] = Some(x * 3)
这些函数运行良好,调用foo(3)
返回Some(5)
,并调用bar(3)
返回Some(9)
,我们都很高兴.
但是现在你遇到了需要你不止一次做这个操作的情况.
foo(3).map(x => foo(x)) // or just foo(3).map(foo) for short
干完了,对吗?
除了不是真的.上面的xpression的输出是Some(Some(7))
,而不是Some(7)
,如果你现在想要在最后链接另一个地图,你不能,因为foo
并bar
采取Int
,而不是Option[Int]
.
输入 flatMap
foo(3).flatMap(foo)
将返回Some(7)
,和
foo(3).flatMap(foo).flatMap(bar)
退货Some(15)
.
这很棒!使用flatMap
允许您链形状的函数A => M[B]
,以遗忘(在前面的示例中A
和B
是Int
,和M
是Option
).
从技术上讲,更多; flatMap
和bind
具有该签名M[A] => (A => M[B]) => M[B]
,这意味着它们采取一个"包装"的值,例如Some(3)
,Right('foo)
或List(1,2,3)
通过通常会采取展开的值,如上述的功能推它foo
和bar
.它通过首先"展开"值,然后将其传递给函数来完成此操作.
我已经看到了用于此的盒子类比,所以请观察我熟练绘制的MSPaint插图:
这种展开和重新包装行为意味着如果我要引入第三个函数但不返回Option[Int]
并尝试将flatMap
其添加到序列中,它将无法工作,因为flatMap
期望您返回一个monad(在本例中为an Option
)
def baz(x: Int): String = x + " is a number" foo(3).flatMap(foo).flatMap(bar).flatMap(baz) // <<< ERROR
要解决这个问题,如果你的函数没有返回monad,你只需要使用常规map
函数
foo(3).flatMap(foo).flatMap(bar).map(baz)
哪个会回来 Some("15 is a number")