7 ms·
Applicative Functors
- lmm 12y agoThe 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.
- eru 12y agoThat's also why Applicatives combine better.
- tel 12y agoApplicatives can be analyzed without outside information. Applicatives are also closed under composition (and thus there's no need for the entire "transformer" concept). As far as I'm aware, nearly every functor admits at least one applicative structure and oftentimes many choices thereof.
- sfvisser 12y ago>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
- tosyx 12y agoI 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 12y agoThat 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 12y agoEven 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.
- judk 12y agoState is sequential when it matters x <- get put y z <- get
- tel 12y agoA good example demonstrating this is the "reverse state monad" http://lukepalmer.wordpress.com/2008/08/10/mindfuck-the-reverse-state-monad/ http://lukepalmer.wordpress.com/2008/08/10/mindfuck-the-reve... It sends state updates "back in time" to compute fixed points. The above article shows how you can compute the fibonacci stream using it.
- dragonwriter 12y ago> 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.
- deleted 12y ago[deleted]
- thinkpad20 12y agoGreat 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 12y agoThanks, fixed!
- dcre 12y agoWonderful 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 12y agoThanks! Both fixed.