为什么应用型仿函数有副作用,但仿函数不能?(Why can applicative functors

2019-07-18 22:16发布

我感觉挺傻问这个问题,但它一直在我的脑海了一会儿,我找不到任何答案。

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

也许他们可以和我只是从来没有注意到......?

Answer 1:

这个答案是有点过于简单化的,但如果我们定义副作用计算受着之前的计算,可以很容易地看到, Functor类型类是不够的副作用,只是因为没有办法把多个计算。

class Functor f where
    fmap :: (a -> b) -> f a -> f b

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

然而,适用函子增加了两个新的功能, pure<*>

class Functor f => Applicative f where
    pure   :: a -> f a
    (<*>)  :: f (a -> b) -> f a -> f b

所述<*>是至关重要的区别在这里,因为它允许我们链中的两个计算: f (a -> b)其产生的功能的计算)和fa的计算,提供了应用该函数的参数。 使用pure<*>它可以定义如

(*>) :: f a -> f b -> f b

它简单地链两次计算,从第一个丢弃最终结果(但可能施加“副作用”)。

因此,在短期,它的能力链的计算是对的效果,如在计算可变状态的最低要求。



Answer 2:

It is not true that Functors don't have effects. Every Applicative (and every Monad through WrappedMonad) is a Functor. The main difference is that Applicative and Monad give you tools how to work with those effects, how to combine them. Roughly

  • Applicative allows you to sequence effects and combine values inside.
  • Monad in addition allows you to determine a next effect according to the result of a previous one.

However Functor only allows you to modify the value inside, it doesn't give tools to do anything with the effect. So if something is just Functor and not Applicative, it doesn't mean it doesn't have effects. It just doesn't have a mechanism how to combine them in this way.

Update: As an example, consider

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

This is clearly a Functor instance that carries effects. It's just that we don't have a way how to define operations with these effects that would comply to Applicative. Unless we impose some additional constraints on r, there is no way how to define an Applicative instance.



Answer 3:

其他答案在这里已经理所当然地表明,仿函数不允许的副作用,因为它们不能被合并或测序,这是在大的很正确,但测序函子的一种方法:通过去向内。

让我们写一个有限的作家仿函数。

data Color    = R    | G    | B
data ColorW a = Re a | Gr a | Bl a deriving (Functor)

然后应用免费单子类型吧

data Free f a = Pure a | Free (f (Free f a))

liftF :: Functor f => f a -> Free f a
liftF = Free . fmap Pure

type ColorWriter = Free ColorW

red, blue, green :: a -> ColorWriter a
red   = liftF . Re
green = liftF . Gr
blue  = liftF . Bl

当然,通过免费的财产,这形成了一个单子,但是效果真的从函子的“层”来了。

interpretColors :: ColorWriter a -> ([Color], a)
interpretColors (Pure a) = ([], a)
interpretColors (Free (Re next)) = let (colors, a) = interpretColors next
                                   in (R : colors, a)
...

所以,这是那种一招。 真的是“计算”正在由自由单子但计算量,隐藏的背景材料介绍,只由一函子介绍。 原来,你可以用任何数据类型做到这一点,它甚至不需要一个函子,但仿函数提供了构建它一个明确的办法。



Answer 4:

让我们先来命名的副作用 的影响 。 各种价值观可以有效果。 一个仿函数是一个类型,它允许你映射任何由效应产生了一个功能。

当一个函子是不是应用性它不允许你使用某种成分风格的影响。 让我们选择一个(人为)例如:

data Contrived :: * -> * where
    AnInt :: Int -> Contrived Int
    ABool :: Bool -> Contrived Bool
    None  :: Contrived a

这是很容易仿函数:

instance Functor Contrived where
    fmap f (AnInt x) = AnInt (f x)
    fmap f (ABool x) = ABool (f x)
    fmap _ None      = None

然而,因为没有合理的执行pure ,所以这个类型不是一个适用函子。 它类似于Maybe ,它具有这样的效果可能没有结果值。 但你不能使用应用性组合程序撰写它。



文章来源: Why can applicative functors have side effects, but functors can't?