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.
.........your hard drive C: has been successfully reformatted.
"OK I didn't"