While playing with monads I often incur in problems of evaluation. Now, I understand the basic concepts of lazy evaluation, but I don't get how monads are lazily evaluated in Haskell.
Consider the following code
module Main where
import Control.Monad
import Control.Applicative
import System
main = print <$> head <$> getArgs
In my mind it should the main function should print the first console argument, but it doesn't.
I know that
getArgs :: IO [String]
head <$> getArgs :: IO String
print <$> (head <$> getArgs) :: IO (IO ())
main :: IO (IO ())
so apparently, the first argument is not printed on the stdout because the content of the first monad IO is not evaluated. So if I join the two monads, it works.
main = join $ print <$> head <$> getArgs
Would anyone, please, clarify it for me? (or give me a pointer)
Haskell 2010 Report (the language definition) says:
The value of the program is the value of the identifier
main
in moduleMain
, which must be a computation of typeIO τ
for some typeτ
. When the program is executed, the computationmain
is performed, and its result (of typeτ
) is discarded.
Your main
function has type IO (IO ())
. The quote above means that only the outer action (IO (IO ())
) is evaluated, and its result (IO ()
) is discarded. How did you get here? Let's look at the type of print <$>
:
> :t (print <$>)
(print <$>) :: (Show a, Functor f) => f a -> f (IO ())
So the problem is that you used fmap
in conjunction with print
. Looking at the definition of Functor
instance for IO
:
instance Functor IO where
fmap f x = x >>= (return . f)
you can see that that made your expression equivalent to (head <$> getArgs >>= return . print)
. To do what you originally intended, just remove the unnecessary return
:
head <$> getArgs >>= print
Or, equivalently:
print =<< head <$> getArgs
Note that IO actions in Haskell are just like other values - they can be passed to and returned from functions, stored in lists and other data structures, etc. An IO action is not evaluated unless it's a part of the main computation. To "glue" IO actions together, use >>
and >>=
, not fmap
(which is typically used for mapping pure functions over values in some "box" - in your case, IO
).
Note also that this has to do not with lazy evaluation, but purity - semantically, your program is a pure function that returns a value of type IO a
, which is then interpreted by the runtime system. Since your inner IO
action is not part of this computation, the runtime system just discards it. A nice introduction to these issues is the second chapter of Simon Peyton Jones's "Tackling the Awkward Squad".
You have head <$> getArgs :: IO String
and print :: Show a => a -> IO ()
, i.e. a value in a monad and a function from a plain value to a monad. The function used to compose such things is the monadic bind operator (>>=) :: Monad m => m a -> (a -> m b) -> m b
.
So what you want is
main = head <$> getArgs >>= print
(<$>)
aka fmap
has the type Functor f => (a -> b) -> f a -> f b
, so it is useful when you want to apply a pure function to some value in a monad, which is why it works with head
but not with print
, since print
is not pure.
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