More fun with applicative functors

前端 未结 5 633
我在风中等你
我在风中等你 2020-12-28 17:48

Earlier I asked about translating monadic code to use only the applicative functor instance of Parsec. Unfortunately I got several replies which answered the question I lite

5条回答
  •  误落风尘
    2020-12-28 18:21

    You can view functors, applicatives and monads like this: They all carry a kind of "effect" and a "value". (Note that the terms "effect" and "value" are only approximations - there doesn't actually need to be any side effects or values - like in Identity or Const.)

    • With Functor you can modify possible values inside using fmap, but you cannot do anything with effects inside.
    • With Applicative, you can create a value without any effect with pure, and you can sequence effects and combine their values inside. But the effects and values are separate: When sequencing effects, an effect cannot depend on the value of a previous one. This is reflected in <*, <*> and *>: They sequence effects and combine their values, but you cannot examine the values inside in any way.

      You could define Applicative using this alternative set of functions:

      fmap     :: (a -> b) -> (f a -> f b)
      pureUnit :: f ()
      pair     :: f a -> f b -> f (a, b)
      -- or even with a more suggestive type  (f a, f b) -> f (a, b)
      

      (where pureUnit doesn't carry any effect) and define pure and <*> from them (and vice versa). Here pair sequences two effects and remembers the values of both of them. This definition expresses the fact that Applicative is a monoidal functor.

      Now consider an arbitrary (finite) expression consisting of pair, fmap, pureUnit and some primitive applicative values. We have several rules we can use:

      fmap f . fmap g           ==>     fmap (f . g)
      pair (fmap f x) y         ==>     fmap (\(a,b) -> (f a, b)) (pair x y)
      pair x (fmap f y)         ==>     -- similar
      pair pureUnit y           ==>     fmap (\b -> ((), b)) y
      pair x pureUnit           ==>     -- similar
      pair (pair x y) z         ==>     pair x (pair y z)
      

      Using these rules, we can reorder pairs, push fmaps outwards and eliminate pureUnits, so eventually such expression can be converted into

      fmap pureFunction (x1 `pair` x2 `pair` ... `pair` xn)
      

      or

      fmap pureFunction pureUnit
      

      So indeed, we can first collect all effects together using pair and then modify the resulting value inside using a pure function.

    • With Monad, an effect can depend on the value of a previous monadic value. This makes them so powerful.

提交回复
热议问题