Hacker News new | ask | show | jobs
by davdar 4156 days ago
I'm curious, what do you mean they compose better and allow for analysis [better than monads]?
2 comments

You can determine a lot more about a computation described only in terms of an applicative functor as you can from a monadic one, without running any of its effects (i.e. "statically").

It's easy to see why when you consider the opaqueness of the function in the right-hand side of a bind. Check out http://gergo.erdi.hu/blog/2012-12-01-static_analysis_with_ap... for an example.

gergoerdi covered analysis, but composition is simple. For any two Applicatives F and G and value type A the following 4 values are all Applicative values

    F A
    G A
    F (G A)
    G (F A)
But for monads only the first two are monads for general monads F and G. So, Applicatives compose better!
And to get F (G A) with Monads, F needs to be a Monad transformer.

In addition to composition, products of Applicatives are also Applicatives:

(F A, G A)

Even for monad transformers it's not necessarily the case that direct composition leads to a monad. For instance, ContT looks like

    newtype ContT r m a = ContT ((a -> m r) -> m r)
so ContT M A is not the same as Cont (M A).

Monads also compose under products as two parallel monadic computations

    data (f * g) a = Pair (f a) (g a)

    p1 :: (f * g) a -> f a
    p1 (Pair fa _) = fa

    p2 :: (f * g) a -> g a
    p2 (Pair _ ga) = ga

    instance (Monad f, Monad g) => Monad (f * g) where
      return a = Pair (return a) (return a)
      Pair fa ga >>= k = Pair (fa >>= p1 . k) (ga >>= p2 . k)
You can also talk about composition under sums

    data (f + g) a = Inl (f a) | Inr (g a)
and you'll get compositions of applicatives here so long as you have a notion of natural transformation

    class Natural f g where
      phi :: f a -> g a

    instance (Applicative f, Applicative g, Natural f g) => Applicative (f + g) where
      pure a = Inl (pure a)
      Inl ff <*> Inl fa = Inl (    ff <*>     fa)
      Inr gf <*> Inr ga = Inr (    gf <*>     ga)
      Inl ff <*> Inr ga = Inr (phi ff <*>     ga)
      Inr gf <*> Inl fa = Inr (    gf <*> phi fa)
but there's no way to get a similar monad. It turns out that you have to "know what branch to go down before you start" in exactly the kind of way applicatives allow and monad preclude.