[Haskell-cafe] Lifting makes lazy

Iavor S. Diatchki diatchki at cse.ogi.edu
Thu Sep 16 13:04:29 EDT 2004

the types IO(IO ()) and IO() are not the same.

think of a value of type "IO a" as a _program_ that when executed
will return a result of type "a" (and while executing may print some 
stuff to the screen).

now consider these types:
putStrLn :: String -> IO ()

this is a _pure function_ which when given a string will return a 
_program_ that later when
executed will print the string.  note however that this function does 
not itself print,
it simply creates a program that can print.   this is the main idea that 
allows haskell
to be pure and still manage to have printing.

liftM :: (a -> b) -> IO a -> IO b

this again is a pure function, that when given a function and a program, 
will make
another program that behaves as follows (when executed):
   * it first executes the argument program to get an "a"
   * then it applies the argument function, to turn the "a" into a "b", 
and this is the final result.

at this point you might wonder: well it is all very good that one can 
build these programs, but how do you execute them?
a slightly simplified answer is as follows:  the haskell run time system 
starts by evaluating
the _expression_ "main :: IO ()", i.e. it will first build an IO 
program, and then (behind the scenece) it will execute it.
the reason this is slightly simlified is that in fact the building of 
the program and its execution kind of
progress in parallel (for more details see Simon PJ's "tackling the 
awkward squad" paper). 

now back to your question, you were wondering about:
liftM putStrLn (readFile "SomeFile")  :: IO (IO ())

this is a peculiar beast --- a program, that when executed will return 
as a result another program
(a kind of meta-program)
when executed this program will behave as follows:
   * it will execute the (readFile "SomeFile") program, and as a result 
it will get the contents of the file
   * then to get the result it will apply putStrLn to this, which will 
produce _another program_
       (rememebr putStrLn does not print, as haskell is a pure language)
at this point this new program will be returned as the result.  notice 
that the only IO that happened
was the readiong of the file.

to sequence IO programs you should use on of the following: (>>=), 
(=<<), or simply the "do"-notation.
so the above program that will first read the file, and then print its 
contents may be written as follows:

do string <- readFile "SomeFile"
     putStrLn string

or alternatively:

putStrLn =<< readFile "SomeFile"

sorry for the long post, and i hope it was useful.

hjgtuyl at chello.nl wrote:

> L.S.,
> In my enthusiasm to reduce imperative style coding to a minimum, I 
> changed  a program to something too lazy to do anything. The following 
> is an  extremely simplified version of the program:
>> import Monad
>> displayFile1 :: IO (IO ())displayFile1 =  liftM putStr contents --  
>> Displays nothing
>>     where
>>       contents :: IO [Char]
>>       contents = readFile "DisplayFile.lhs"
> This should display the contents of a file, but nothing appears. The  
> following function should be exactly the same, but this one does 
> display  the file:
>> displayFile2 :: IO ()
>> displayFile2 =  do
>>     contents <- readFile "DisplayFile.lhs"
>>     putStr contents
> My conclusion is, that "putStr" is evaluated strictly, while "liftM  
> putStr" is not.
> I have the following questions:
>  - Why is this difference?
>  - Is there some method to predict whether my program is sufficiently  
> strict to really do what it is supposed to do?
>  - Did someone design a method to develop programs not too strict and 
> not  too lazy?
>  - The manual "A gentle introduction to Haskell" states in section 
> 6.3:  "adding strictness flags may lead to hard to find infinite loops 
> or have  other unexpected consequences"; I would like to know when 
> these problems  arise; are these cases described somewhere?

More information about the Haskell-Cafe mailing list