为什么applicative functor有副作用,但是functor不能?

Bri*_*ian 16 monads haskell side-effects applicative

我觉得这个问题很傻,但是我已经想到了一段时间,我找不到任何答案.

所以问题是:为什么应用仿函数有副作用,但仿函数不能?

也许他们可以而且我从未注意到......?

Pet*_*lák 26

Functors没有效果是不正确的.每一个Applicative(每MonadWrappedMonad)是Functor.主要的区别是,ApplicativeMonad给你的工具如何与这些效果,如何将它们结合起来的工作.大致

  • Applicative 允许您对效果进行排序并将值组合在一起.
  • Monad 此外,您还可以根据前一个效果确定下一个效果.

但是Functor只允许你修改里面的值,它不会给工具做任何有效的事情.所以,如果事情仅仅是Functor,不Applicative,这并不意味着它没有影响.它只是没有一种机制如何以这种方式组合它们.

更新:举个例子,考虑一下

import Control.Applicative

newtype MyF r a = MyF (IO (r, a))

instance Functor (MyF r) where
    fmap f (MyF x) = MyF $ fmap (fmap f) x
Run Code Online (Sandbox Code Playgroud)

这显然是一个Functor带有效果的实例.只是我们没有办法如何定义符合这些效果的操作 Applicative.除非我们施加一些额外的约束r,否则无法定义Applicative实例.


sha*_*ang 25

这个答案有点过分简化,但如果我们将副作用定义为计算受到先前计算的影响,很容易看出Functor类型类不足以产生副作用,因为无法链接多个计算.

class Functor f where
    fmap :: (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

仿函数唯一能做的就是通过一些纯函数来改变计算的最终结果a -> b.

但是,应用程序函数添加了两个新函数,pure<*>.

class Functor f => Applicative f where
    pure   :: a -> f a
    (<*>)  :: f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

<*>是至关重要的区别,因为它允许我们链接两个计算:( f (a -> b)产生函数f a的计算)和提供应用函数的参数的计算.使用pure并且<*>可以定义例如

(*>) :: f a -> f b -> f b
Run Code Online (Sandbox Code Playgroud)

这简单地链接两个计算,从第一个计算中丢弃最终结果(但可能应用"副作用").

简而言之,它是链计算的能力,这是计算中可变状态等效果的最低要求.

  • 如果你"将副作用定义为受先前计算影响的计算",那么你可以认为Applicative也没有副作用 - 你需要一个完整的monad.Applicative将它们链接在一起,但是Monad允许第一个输出影响第二个执行的动作.我现在想不出一种方法可以改写一下,说出我认为你的意思.(我认为你的意思是`翻转const <$> writeFile"temp.txt""Hello"<*> readFile"temp.txt"`其中第一个动作影响第二个结果.) (2认同)
  • 是的,可以更准确地说,对于应用程序,计算的最终结果可能会受到前面的计算的影响.与monad相反,monad可以使用先前计算的最终结果来选择下一个计算. (2认同)