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?
A side effect is when a function relies on, or modifies, something outside its parameters to do something. For example, a function which reads or writes from a variable outside its own arguments, a database, a file, or the console can be described as having side effects.
Any operation that is not directly related to the final output of the function is called a Side Effect . Now let us see an impure function where we mutate the input and do something that we are not supposed to in a pure function.
The applicative equivalent for >>
is *>
, so you can do
ghci> :m Control.Applicative
ghci> print 5 *> print 7
5
7
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 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"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With