
Applicative Functors - lelf
http://pbrisbin.com/posts/applicative_functors/
======
tosyx
I like to think of Applicative Functors, Monads, etc as the Design Patterns of
functional languages (though I've developed most of my understanding by
applying these ideas in JavaScript).

A nice way to think about how they can be used (and the difference between
parallel and sequential evaluation discussed towards the end of the article)
is in handling promises:

The parallel or sequential composition of a list of promises returns a promise
of a list.

The monadic composition resolves each promise sequentially (via the Haskell
operator `sequence` or as a cascade of JavaScript `.then` calls).

The parallel applicative composition resolves all promises simultaneously.

~~~
arianvanp
That is a misconceptio and is only possible if you break monad laws. Monads
aren't about sequencing. Just some monads are.

    
    
        (+) <$> a <*> b 
    

And

    
    
        x <- a
        y <- b
        return (a  + b) 
    
    

Will yield exactly the same behaviour. Even in evaluation order. And they can
be interchanged. It may look like we are sequencing in the second example but
that's only true in datatype with sequencing embodied in their monad and
application implementation ( State monad). An example of a datatype that
doesn't have this property is [a]

~~~
wyager
Even the state monad is not actually sequential. It just appears that way
conceptually. It can, just like most haskell code, end up being computed in
any order (because of laziness and lambda calculus reordering rules).

For the most part, only "special" monads like IO enforce sequential
computation.

~~~
dragonwriter
> For the most part, only "special" monads like IO enforce sequential
> computation.

No, IO doesn't either, because of laziness. If you need a demonstration, then,
in the IO monad, open a file (using the functions in System.IO), read the
contents (without doing anything else that depends on them), close the file,
and then use the file contents.

IO still works in data dependency order.

~~~
tel
That behavior can be seen as a perversion of IO's behavior—it's roughly known
as Lazy IO and is considered convenient but potentially very dangerous.

The whole cottage industry of streaming monadic operators like enumeratees,
pipes, conduits, io-streams, etc takes "fixing" Lazy IO as a major design goal
or inspiration.

Lazy IO depends upon using the function `unsafeInterleaveIO` which, as its
name suggests, is considered potentially unsafe---and your example
demonstrates why!

------
thinkpad20
Great article, but there is one nitpick: you need parentheses in the signature
for bind. It should look like

    
    
        (>>=) :: Monad m  -- for any monad,
              => m a      -- take wrapped value
              -> (a -> m b) -- and a function which needs it unwrapped
              -> m b      -- unwrap it, and apply that function
    

Or, written the other way:

    
    
        (>>=) :: Monad m           -- for any monad,
              => m a               -- take a wrapped value
              -> ((a -> m b) -> m b) -- and return a function which can take an 
                               -- unwrapped value and a wrapped one and 
                               -- return another wrapped one
    
    

Which also makes the comments on the second one a bit inaccurate. It should
read "return a function which takes a (function from an unwrapped value to a
wrapped one) and returns another wrapped one."

~~~
patbrisbin
Thanks, fixed!

------
dcre
Wonderful piece. A small typo: "Fuctor" in the first code sample.

Edit: I think "dependant" is also a typo. Apparently in British English it is
used to mean a dependent in the sense of a child who relies on a parent for
support, but when it's used as an adjective, "dependent" is still the correct
spelling, even in British English.

~~~
patbrisbin
Thanks! Both fixed.

------
lmm
The example applicative still respects sequencing - to do what the last
paragraph claims to be doing you'd need to use some kind of nondeterministic
gather.

Honestly I think the differences in that example are pretty superficial. The
real advantage of applicative is that because the assumptions are weaker, some
things that aren't Monads are Applicatives.

~~~
sfvisser
>Honestly I think the differences in that example are pretty superficial.

For most instances the difference is superficial, but for some problem domains
the difference can have a pretty big impact.

All of this is because Applicative composition (using apply) statically
guarantees there is no data dependencies between the (possibly) effectful
computations. Monadic composition (using bind) can (but might not) introduce a
data dependency which can only be reasoned about at runtime.

Facebook's Haxl project is an interesting example of this:
[http://www.cs.ox.ac.uk/ralf.hinze/WG2.8/31/slides/simon.pdf](http://www.cs.ox.ac.uk/ralf.hinze/WG2.8/31/slides/simon.pdf)

