monad 是一个“事物”,它接受一个上下文(我们称之为 m)并“产生”一个值,同时仍然尊重 monad 法则。我们可以将其视为单子的“内部”和“外部”。单子定律告诉我们如何处理“往返”——出去然后回到里面。特别是,定律告诉我们 m (ma) 本质上与 (ma) 是同一类型。
关键是 monad 是这种往返事物的概括。join squashes (m (ma))'s into (ma)'s, and (>>=) 从 monad 中取出一个值并将一个函数应用到 monad 中。换句话说,它将一个函数 (f :: a -> mb) 应用到 (ma) 中的 a —— 这会产生一个 (m (mb)),然后通过 join 将其压缩以获得我们的 (mb)。
那么这与“获取”和对象有什么关系呢?
好吧,do 符号设置我们,以便计算结果在我们的 monad 中。并且 (<-) 让我们从 monad 中提取一个值,以便我们可以将它绑定到一个函数,同时名义上仍然在 monad 内部。因此,例如:
doStuff = do
a <- get
b <- get
return $ (a + b)
注意 a 和 b 是纯的。它们在 get 的“外部”,因为我们实际上窥视了它的内部。但是现在我们在 monad 之外有了一个值,我们需要对它做一些事情 (+),然后将它放回 monad 中。
这只是一点暗示性的符号,但如果我们可以这样做可能会很好:
doStuff = do
a <- get
b <- get
(a + b) -> (\x -> return x)
真正强调它的来回。当你完成一个单子动作时,你必须在该表的右列,因为当动作完成时,'join'将被调用以展平图层。(至少在概念上)
哦,对了,对象。好吧,很明显,OO 语言基本上是在某种 IO monad 中生存和呼吸的。但我们实际上可以进一步分解它。当您按照以下方式运行时:
x = foo.bar.baz.bin()
你基本上是在运行一个 monad 转换器堆栈,它接受一个 IO 上下文,它产生一个 foo 上下文,它产生一个 bar 上下文,它产生一个 baz 上下文,它产生一个 bin 上下文。然后运行时系统根据需要多次“调用”加入这个东西。请注意这个想法与“调用堆栈”的结合程度。事实上,这就是为什么它在 haskell 方面被称为“monad 转换器堆栈”。它是一堆单子上下文。