[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
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,
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 =<< readFile "SomeFile"
sorry for the long post, and i hope it was useful.
hjgtuyl at chello.nl wrote:
> 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
>> 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