Evaluation of Haskell and the Lazy Monads

When playing with monads, I often experience evaluation problems. Now I understand the basic concepts of lazy evaluation, but I do not understand 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 opinion, the main function should print the first argument to the console, but that is not the case.

I know that

 getArgs :: IO [String] head <$> getArgs :: IO String print <$> (head <$> getArgs) :: IO (IO ()) main :: IO (IO ()) 

apparently, the first argument is not printed on stdout, because the contents of the first IO monad are not evaluated. Therefore, if I join the two monads, this works.

 main = join $ print <$> head <$> getArgs 

Anyone please clarify this for me? (or give me a pointer)

+7
source share
2 answers

A Haskell 2010 report (language definition) says :

The program value is the value of the main identifier in the main module, which should be a calculation of type IO ฯ„ for some type ฯ„ . When the program is executed, the calculation of main is equal and its result (of type ฯ„ ) is discarded.

Your main function is of type IO (IO ()) . The above quote means that only external action is evaluated ( IO (IO ()) ), and its result ( IO () ) is discarded. How did you get here? Let's look at the type print <$> :

 > :t (print <$>) (print <$>) :: (Show a, Functor f) => fa -> f (IO ()) 

So the problem is that you used fmap in combination with print . Considering the definition of Functor instance for IO :

 instance Functor IO where fmap fx = x >>= (return . f) 

you can see that this made your expression equivalent (head <$> getArgs >>= return . print) . To accomplish what you originally planned, simply remove the unnecessary return :

 head <$> getArgs >>= print 

Or, equivalently:

 print =<< head <$> getArgs 

Note that IO actions in Haskell are similar to other values โ€‹โ€‹- they can be passed and returned from functions, stored in lists and other data structures, etc. An IO action is not evaluated if it is not part of the main calculation. To "glue" the actions of the IO together, use >> and >>= , not fmap (which is usually used to display pure functions over values โ€‹โ€‹in a certain "field" - in your case, IO ).

Please also note that this should not be related to lazy evaluation, but purity is 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 internal IO action is not part of this calculation, the runtime system simply discards it. A good introduction to these problems is the second chapter of Simon Payton Jones, "Shooting a Clumsy Squad . "

+11
source

You have head <$> getArgs :: IO String and print :: Show a => a -> IO () , that is, the value in the monad and a function from a simple value to a monad. The function used to compose such things is the monadic binding operator (>>=) :: Monad m => ma -> (a -> mb) -> mb .

So you want

 main = head <$> getArgs >>= print 

(<$>) aka fmap is of type Functor f => (a -> b) -> fa -> fb , so it is useful if you want to apply a pure function to some value in the monad, so it works with head , but not with print > since print not clean.

+4
source

All Articles