Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle side effect with Applicative?

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?

like image 528
mb14 Avatar asked Jun 28 '14 14:06

mb14


People also ask

What are side effects in functional programming?

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.

What are Javascript 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.


1 Answers

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"
like image 173
AndrewC Avatar answered Oct 10 '22 02:10

AndrewC