Here's two really simple functions f and g.
{-# LANGUAGE ScopedTypeVariables #-}
module Test where
import Control.Applicative
f :: IO ()
f = do
y <- (<*>) (pure (show . (*10))) (read <$> readFile "data")
writeFile "out" y
g :: IO ()
g = do
y <- (readFile "data" >>= return . show . (*10) . read)
writeFile "out" y
The file read and *10 in fis written in the applicative style with pure and (<*>). The file read and *10 in g is written in the monadic style, with >>=. (I have deliberately avoided using liftM in g to emphasize the question below).
What is the semantic difference between f and g? Or in this case, is it just a stylistic choice?
show . (*10) . read is a "non-monadic" function - by which I mean, it does not do anything in the IO monad, as you can see from its type.
>>= return . can be shortened to
`liftM`
But
`liftM`
should always be equivalent to
`fmap`
fmap neither needs the monad typeclass nor the applicative typeclass, it merely needs the functor typeclass.
Now turning our attention to the applicative version, this:
(<*>) (pure ...
is equivalent to <$>, which is just fmap.
So in both cases we are "really" just working with a functor operation, inbetween reading and writing, and although you have combined the functions in a slightly different way (we'd need to apply one or more "laws" to translate the two versions into each other), the semantics are - or should be - identical. Certainly they are with the IO monad, anyway.
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