假设我们有一个一元序列:
doSomething = do
a <- f
b <- g
c <- h
pure (a, b, c)
Run Code Online (Sandbox Code Playgroud)
我们可以使用 applicative functor 轻松地重写它:
doSomething2 = (,,) <$> f <*> g <*> h
Run Code Online (Sandbox Code Playgroud)
但是如果 monadic 序列看起来像这样:
doSomething' n = do
a <- f n
b <- g a
c <- h b
pure (a, b, c)
Run Code Online (Sandbox Code Playgroud)
还有可能在那里使用 applicative 吗?如果不是,障碍是什么?(还有一本书写的,尽管如此,我们可以使用 applicative 和join一起使用,但我不知道如何使用)。
不,这正是 Monad 相比 Applicative 带来的额外力量。一个 monadic 或 applicative 类型的值f a可以被认为是两部分:“效果”(发生在f数据结构中的东西)和“值”(发生在 type 值中的东西a)。使用 Applicative,效果不可能依赖于值,因为 Applicative 中的函数无法将函数的结果(值)编织回效果中。(>>=)Monad 中的函数为您提供了这种能力;join同样强大。
关键是 for 的签名(>>=)包含一个看起来像这样的函数(a -> m b):您可以查看纯值( a),然后(m b)根据该值选择效果。相比
(>>=) :: Monad m => (a -> m b) -> m a -> m b
Run Code Online (Sandbox Code Playgroud)
到
fmap :: Functor f => (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)
最后两个只接受完全在值领域运行的函数:(a -> b),因此无法根据值确定结构/效果。
当然,您仍然可以对这种do表示法进行脱糖,但您必须在结果中使用 monadic 操作:
doSomething'' n =
f n >>= (\a ->
g a >>= (\b ->
h b >>= (\c ->
pure (a, b, c))))
Run Code Online (Sandbox Code Playgroud)