I see everywhere that Applicative
can handle side effects, but all the simple examples I've seen are just combining stuff together like:
> (,,) <$> [1,2] <*> ["a", "b", "c"] <*> ["foo", "bar"]
[(1,"a","foo"),(1,"a","bar"),(1,"b","foo"),(1,"b","bar"),
(1,"c","foo"),(1,"c","bar"),(2,"a","foo"),(2,"a","bar"),
(2,"b","foo"),(2,"b","bar"),(2,"c","foo"),(2,"c","bar")]
Which is cool but I can't see how that links to side effects. My understanding is that Applicative
is a weak monad and so you can handle side effects (as you would do with a State monad) but you can't reuse the result of the previous side effect.
Does that mean that >>
could be written for an Applicative
and things like
do
print' "hello"
print' "world"
would make sense (with print' :: a -> Applicative something
) (with the appropriate do-applicative extension).
In other world, is the difference between Monad
and Applicative
is that Monad
allows x <- ...
but Applicative
doesn't.
Then, is the Writer monad, just an applicative?
Output
The applicative equivalent for
>>
is*>
, so you can doInput - a better case for Applicative
Which works nicely for input:
Notice that since we're using Applicative for IO, we're in the IO monad anyway, so can use
>>=
if we like. The benefit Applicative gives you is the nice syntax.My favourite is with parsing, so I can do
The difference between Applicative and Monad
The difference between Applicative and Monad is that Monad has
>>=
, which lets you choose what side effect to use based on the value you have.Using Monad:
(There's no
System.IO.reformat
orWinXP.Dialogs.ask
. This is just an example I found funny.)Using Applicative:
Sadly, using Applicative I can't inspect the Boolean value to determine whether to reformat or not – the side effect order is determined at compile time, in an Applicative, and the hard drive will always be reformatted with this piece of code. I need the Monad's bind (
>>=
) to be able to stop the reformat.In your example, the
Applicative
instance for lists is being used. The "effect" here is nondeterminism: returning more than one possible value.The
Applicative
instance for lists calculates the possible combinations of elements from the individual list arguments.However, what it can't do is make one of the lists depend on values contained in a previous list. You need the
Monad
instance for that.For example, consider the code:
Here we are generating lists whose values depend on a list that appeared earlier in the computation (the
[2,7]
one). You can't do that withApplicative
.An analogy
I hope the following analogy is not too obtuse, but
Applicative
s are like railroads.In an
Applicative
, effects build the railroad upon which the locomotive of function application will travel. All the effects take place while building the railroad, not when the locomotive moves. The locomotive cannot change its course in any way.A
Monad
is like having a locomotive in a railroad which is still under construction. Passengers can actually yell to the construction crew a few meters ahead to tell them things like "I like the scenery on this side, please lay the tracks a bit more towards the right" or "you can stop laying tracks, I get out right here". Of course, for this strange railway, the company can't provide a timetable or a fixed list of stops that can be checked before embarking on the train.That's why
Applicative
s can be regarded as a form of "generalized function application", butMonad
s can't.Applicative and Monad both provide ways of "combining" multiple side-effectful1 values into a single side-effectful value.
The Applicative interface for combining just lets you combine effectful values such that the resulting effectful value combines all their effects according to some "fixed" recipe.
The Monad interface for combining lets you combine effectful values in such a way that the effects of the combined value depends on what the original effectful values do when they're actually resolved.
For example, the
State Integer
monad/applicative is of values that depend upon (and affect) someInteger
state.State Integer t
values only have a concrete value in the presence of that state.A function that takes two
State Integer Char
values (call thema
andb
) and gives us back aState Integer Char
value and only uses the Applicative interface ofState Integer
must produce a value whose "statefulness" is always the same, regardless of what theInteger
state value is and regardless of whatChar
values the inputs yield. For example, it could thread the state througha
and thenb
, combining theirChar
values somehow. Or it could threat the state throughb
and thena
. Or it could pick onlya
or onlyb
. Or it could ignore both entirely, not taking either of their effects on the currentInteger
state, and justpure
some char value. Or it could run either or both of them any fixed number of times in any fixed order, and it could incorporate any otherState Integer t
values it knows about. But whatever it does, it always does that, regardless of the currentInteger
state, or any values produced by any of theState Integer t
values it manages to get its hands on.A function that took the same inputs but was able to use the monad interface for
State Integer
can do much more than that. It can runa
orb
depending on whether the currentInteger
state is positive or negative. It can runa
, then if the resultingChar
is an ascii digit character it can turn the digit into a number and runb
that many times. And so on.So yes, a computation like:
Is one that could be implemented using only the Applicative interface to whatever
print'
returns. You are close to correct that the difference between Monad and Applicative if both had a do-notation would be that monadic do would allowx <- ...
, while applicative do would not. It's a bit more subtle than that though; this would work with Applicative too:What Applicative can't do is inspect
x
andy
to decide whatf
to call on them (or do anything with the result off x y
other than justpure
it.You are not quite correct that there's no difference between
Writer w
as a monad and as an applicative, however. It's true that the monadic interface ofWriter w
doesn't allow the value yielded to depend on the effects (the "log"), so it must always be possible to rewrite anyWriter w
defined using monadic features to one that only uses applicative features and always yields the same value2. But the monadic interface allows the effects to depend on the values, which the applicative interface doesn't, so you can't always faithfully reproduce the effects of aWriter w
using only the applicative interface.See this (somewhat silly) example program:
Then with that loaded in GHCi:
Note how with
divM
, whethernumer
's effects are included innumer `divM` denom
depends on the value ofdenom
(as does whether the effect oftell ["divide by zero"]
). With the best the applicative interface can do, the effects ofnumer
are always included innumer
divAdenom
, even when lazy evaluation should mean that the value yielded bynumer
is never inspected. And it's not possible to helpfully add "divide by 0" to the log when the denominator is zero.1 I don't like to think of "combining effectful values" as the definition of that monads and applicatives do, but it's an example of what you can do with them.
2 When bottoms aren't involved, anyway; you should be able to see from my example why bottom can mess up the equivalence.