我感觉挺傻问这个问题,但它一直在我的脑海了一会儿,我找不到任何答案。
所以,问题是:为什么应用型仿函数有副作用,但仿函数不能?
也许他们可以和我只是从来没有注意到......?
我感觉挺傻问这个问题,但它一直在我的脑海了一会儿,我找不到任何答案。
所以,问题是:为什么应用型仿函数有副作用,但仿函数不能?
也许他们可以和我只是从来没有注意到......?
这个答案是有点过于简单化的,但如果我们定义副作用计算受着之前的计算,可以很容易地看到, 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
它简单地链两次计算,从第一个丢弃最终结果(但可能施加“副作用”)。
因此,在短期,它的能力链的计算是对的效果,如在计算可变状态的最低要求。
It is not true that Functor
s 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.
其他答案在这里已经理所当然地表明,仿函数不允许的副作用,因为它们不能被合并或测序,这是在大的很正确,但测序函子的一种方法:通过去向内。
让我们写一个有限的作家仿函数。
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)
...
所以,这是那种一招。 真的是“计算”正在由自由单子但计算量,隐藏的背景材料介绍,只由一函子介绍。 原来,你可以用任何数据类型做到这一点,它甚至不需要一个函子,但仿函数提供了构建它一个明确的办法。
让我们先来命名的副作用 的影响 。 各种价值观可以有效果。 一个仿函数是一个类型,它允许你映射任何由效应产生了一个功能。
当一个函子是不是应用性它不允许你使用某种成分风格的影响。 让我们选择一个(人为)例如:
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
,它具有这样的效果可能没有结果值。 但你不能使用应用性组合程序撰写它。