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 f
is 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 toBut
should always be equivalent to
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:
is equivalent to
<$>
, which is justfmap
.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.