9

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?

Will Ness
  • 62,652
  • 8
  • 86
  • 167
mb14
  • 20,914
  • 4
  • 54
  • 97
  • 1
    well: https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo :D – Carsten Jun 28 '14 at 14:32
  • @CarstenKönig : +1 for the link. However I'm even more confused now. – mb14 Jun 28 '14 at 14:40
  • 1
    `(>>)` does exist for applicatives and is called `(*>)` so you can do `print "hello" *> print "world"` – Lee Jun 28 '14 at 14:40
  • @mb14: it's just that something like this might be in the comming for one of the next versions of ghc – Carsten Jun 28 '14 at 14:57
  • 2
    @CarstenKönig I normally advise people to switch to Applicative if they're largely using `do` notation like that, since Applicative notation feels much purer and less cluttered. That proposal is the opposite of what I advise! – AndrewC Jun 28 '14 at 15:33
  • @CarstenKönig It's also potentially a problem that it's very unclear from a given syntactic expression whether Monad or Applicative is being used _at any given step_ so the meaning of the code is unclear if the Monad and Applicative sequencing have different implementations, as in the motivating example. – AndrewC Jun 28 '14 at 15:39
  • true - but I just wanted to point to a fun-fact here ;) – Carsten Jun 28 '14 at 16:06
  • 1
    @AndrewC, that does seem weird. I would think it wiser to choose a new key word, like `doA` – dfeuer Jun 28 '14 at 21:06
  • @dfeuer I strongly agree, but the proposal still includes switching to a monad implementation for individual lines that need it, which is inherently unclear. I'm also not convinced that the algorithm proposed always gives the maximum or optimal Applicativeness (or that those are always the same). I'd suggest that they should use a different symbol than ` – AndrewC Jun 28 '14 at 21:55

3 Answers3

22

Output

The applicative equivalent for >> is *>, so you can do

ghci> :m Control.Applicative
ghci> print 5 *> print 7
5
7

Input - a better case for Applicative

import Control.Applicative

data Company = Company {name :: String, size :: Int}
  deriving Show

getCompany :: IO Company
getCompany = Company <$> getLine <*> readLn

Which works nicely for input:

ghci> getCompany >>= print
BigginsLtd
3
Company {name = "BigginsLtd", size = 3}

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

data Statement = Expr Expression | If Condition Statement Statement
parseStatement = Expr <$> parseExpression <|> 
                 If <$> (string "if" *> parseCondition) 
                    <*> (string "then" *> parseStatement)
                    <*> (string "else" *> parseStatement)

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:

don't_reformat_hard_drive :: Bool -> IO ()
don't_reformat_hard_drive yes = if yes then putStr "OK I didn't" 
                                       else putStr "oops!" >> System.IO.reformat "C:/"

maybeReformat :: IO ()
maybeReformat = WinXP.Dialogs.ask "Don't reformat hard drive?" 
               >>= don't_reformat_hard_drive 

(There's no System.IO.reformat or WinXP.Dialogs.ask. This is just an example I found funny.)

Using Applicative:

response :: Bool -> () -> String
response yes () = if yes then "OK I didn't" else "oops!"

probablyReformat = response <$> WinXP.Dialogs.ask "Don't reformat hard drive?"
                            <*> System.IO.reformat "C:\"

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.

Don't reformat hard drive? Yes No

.........your hard drive C: has been successfully reformatted.
"OK I didn't"
Will Ness
  • 62,652
  • 8
  • 86
  • 167
AndrewC
  • 31,331
  • 7
  • 73
  • 113
  • 6
    -1 for stopping the reformat of a harddrive with Windows XP on it. +2 for at least considering it... – leftaroundabout Jun 28 '14 at 16:10
  • 1
    @leftaroundabout Hehe. And I'm delighted that your humorous upvote is the one that made me a 20k user! Thanks. :) – AndrewC Jun 28 '14 at 16:11
  • 1
    It's worth also reading the question [What advantage does Monad give us over an Applicative?](http://stackoverflow.com/questions/17409260/what-advantage-does-monad-give-us-over-an-applicative), which has some great answers on a closely related question. – AndrewC Jun 28 '14 at 22:14
  • 2
    your `reformat` example is the perfect illustration of the difference between Applicative and Monad! Haven't seen it until now. :) :) :) – Will Ness Sep 21 '18 at 14:20
7

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) some Integer 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 them a and b) and gives us back a State Integer Char value and only uses the Applicative interface of State Integer must produce a value whose "statefulness" is always the same, regardless of what the Integer state value is and regardless of what Char values the inputs yield. For example, it could thread the state through a and then b, combining their Char values somehow. Or it could threat the state through b and then a. Or it could pick only a or only b. Or it could ignore both entirely, not taking either of their effects on the current Integer state, and just pure 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 other State Integer t values it knows about. But whatever it does, it always does that, regardless of the current Integer state, or any values produced by any of the State 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 run a or b depending on whether the current Integer state is positive or negative. It can run a, then if the resulting Char is an ascii digit character it can turn the digit into a number and run b that many times. And so on.

So yes, a computation like:

do
  print' "hello"
  print' "world"

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 allow x <- ..., while applicative do would not. It's a bit more subtle than that though; this would work with Applicative too:

do  x <- ...
    y <- ...
    pure $ f x y

What Applicative can't do is inspect x and y to decide what f to call on them (or do anything with the result of f x y other than just pure 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 of Writer w doesn't allow the value yielded to depend on the effects (the "log"), so it must always be possible to rewrite any Writer 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 a Writer w using only the applicative interface.

See this (somewhat silly) example program:

import Control.Applicative
import Control.Monad.Writer

divM :: Writer [String] Int -> Writer [String] Int -> Writer [String] Int
divM numer denom
  = do  d <- denom
        if d == 0
          then  do  tell ["divide by zero"]
                    return 0
          else  do  n <- numer
                    return $ n `div` d


divA :: Writer [String] Int -> Writer [String] Int -> Writer [String] Int
divA numer denom = divIfNotZero <$> numer <*> denom
  where
    divIfNotZero n d = if d == 0 then 0 else n `div` d


noisy :: Show a => a -> Writer [String] a
noisy x = tell [(show x)] >> return x

Then with that loaded in GHCi:

*Main> runWriter $ noisy 6 `divM` noisy 3
(2,["3","6"])
*Main> runWriter $ noisy 6 `divM` noisy 0
(0,["0","divide by zero"])
*Main> runWriter $ undefined `divM` noisy 0
(0,["0","divide by zero"])

*Main> runWriter $ noisy 6 `divA` noisy 3
(2,["6","3"])
*Main> runWriter $ noisy 6 `divA` noisy 0
(0,["6","0"])
*Main> runWriter $ undefined `divA` noisy 0
(0,*** Exception: Prelude.undefined
*Main> runWriter $ (tell ["undefined"] *> pure undefined) `divA` noisy 0
(0,["undefined","0"])

Note how with divM, whether numer's effects are included in numer `divM` denom depends on the value of denom (as does whether the effect of tell ["divide by zero"]). With the best the applicative interface can do, the effects of numer are always included in numerdivAdenom, even when lazy evaluation should mean that the value yielded by numer 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.

Ben
  • 56,956
  • 19
  • 113
  • 151
4

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:

foo :: [Int]
foo = do
        r <- [2,7]
        if (even r)
             then [2,5,6]
             else [9,234,343]

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 with Applicative.

An analogy

I hope the following analogy is not too obtuse, but Applicatives 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 Applicatives can be regarded as a form of "generalized function application", but Monads can't.

BenMorel
  • 30,280
  • 40
  • 163
  • 285
danidiaz
  • 24,322
  • 4
  • 41
  • 79
  • perfect metaphor for the monads! that, or a *pipe network* being built just as the stuff / data flows inside it, based on that flow of stuff / data. --- --- --- maybe Monads *can* be regarded as *even more generalized* function application! :) – Will Ness Sep 21 '18 at 14:29
  • 1
    actually, your `foo` *is* encodable as applicative -- because the *lengths* of the two alternative lists are the same! it is only when the *"shape"* of the next computation can change depending on the values in the previous computation, that it is truly a monadic computation overall. – Will Ness May 02 '19 at 15:05
  • @WillNess An effect (here, nondeterminism) is depending on *values* returned by previous effectful actions. What would be the corresponding `Applicative` expression? – danidiaz May 02 '19 at 18:28
  • 1
    `foo = liftA2 g [2,7], [0,1,2] where g r i = if (even r) then [2,5,6] !! i else [9,234,343] !! i`. :) the *shape* is known in advance, so, it could be done! And if you'd calculated the values inside those lists from `r`, you'd still could calculate them inside `g`. It's only when the *shape* is determined from values, it becomes impossible. So the usual talk about `join` is too simplistic. Some uses of `join` *are* still applicative. It's only when `join` is unavoidable, that the computation is [essentially monadic](https://stackoverflow.com/search?q=user%3A849891+essentially+monadic). :) – Will Ness May 03 '19 at 06:04
  • @WillNess That means different monads not only vary in their context but also in their notion of what constitutes a relevant value in this context. In the non-deterministic List context not the list values themselves are relevant, but their existence and non-existence respectively. Exciting! – scriptum Feb 07 '20 at 11:24
  • @bob [here's something](https://stackoverflow.com/questions/11323300/using-return-vs-not-using-return-in-the-list-monad/11326549#11326549) that I think you'll find relevant. – Will Ness Feb 07 '20 at 14:01