How to handle side effect with Applicative?

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"

Leave a Comment