应用函子方面的一元序列

kir*_*rov 4 haskell

假设我们有一个一元序列:

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一起使用,但我不知道如何使用)。

ama*_*loy 8

不,这正是 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)