how to convert IO String to string---- still have questions

Wolfgang Jeltsch wolfgang@jeltsch.net
Sun, 24 Nov 2002 21:41:16 +0100


Hello,

several mails spoke about converting an IO String to a String. I have to =
point=20
out that such a conversion is not what happens when you use monadic I/O. =
In=20
fact, such a conversion is just not possible in Haskell 98.

Since Haskell is a pure language, evaluating a String expression mustn't =
cause=20
any side effects. Therefore, if we want to have an operation with side=20
effects, we cannot have an expression of type String. Instead we use an=20
expression of type IO String and we mustn't be able to convert an IO Stri=
ng=20
into a String because, otherwise, we could construct a String expression =
for=20
the I/O operation using this conversion facility.

As the nameless guy pointed out, a value of IO String denotes an I/O oper=
ation=20
which has a String as its result. To use this result, you have to combine=
=20
your I/O operation with a function that has String parameter and returns =
an=20
I/O operation. The result of this combination is an I/O operation which=20
consists of executing the operation returning the String, applying the=20
function to it and execution the resulting I/O operation.

I want to explain this with the readFile/putStrLn example. readFile has t=
he=20
type String -> IO String. Strictly speaking, this does not mean that read=
File=20
is an I/O operation which has a String argument and a String result. It m=
eans=20
that it is a function which can be applied to String values and has=20
(different) I/O operations for different arguments as its result. Each of=
=20
these I/O operations is a file reading operation for one specific path na=
me.

If we apply readFile to the string "test.dat", we get an operation which =
reads=20
and returns the content from the respective file. This operation has the =
type=20
IO String.

putStrLn has the type String -> IO (). Applying this function to a String=
=20
value results in an I/O operation which puts the string to the standard=20
output and a newline after it. Every I/O operation has to have exactly on=
e=20
result value. Since putting a line has no meaningful result, the type () =
is=20
used as the result type. The type () is a type which has (apart from _|_=20
which denotes undefindness) the value () as its only value.

Now, having readFile "test.dat" and putStrLn, we can combine these two us=
ing=20
the monadic bind operator >>=3D. Specialized for I/O operations, this ope=
rator=20
has the type IO a -> (a -> IO b) -> IO b. Specialized for our example it =
has=20
the type IO String -> (String -> IO ()) -> IO (). So we are able to write
    readFile "test.dat" >>=3D putStrLn
and get a value of type IO (). This is an I/O operation which first reads=
 the=20
content from "test.dat", applies putStrLn to it which results in an I/O=20
operation outputting the file content, and finally executes this I/O=20
operation so that the content is written to standard output.

Since putStrLn is equivalent to \content -> putStrLn content, we can achi=
eve=20
the same effect by writing
    readFile "test.dat" >>=3D \content -> putStrLn content.
Using the syntactic sugar of the do notation, we get the equivalent expre=
ssion
    do
        content <- readFile "test.dat"
        putStrLn content
which looks quiet like imperative programming but isn't really this.

The most important point is the impossibility of an IO a to a conversion.=
 Once=20
you have an I/O computation you have to use the >>=3D operator to make it=
s=20
result available to other parts of your program. You cannot make it avail=
able=20
by conversion. On the top level, every haskell program has a variable cal=
led=20
main which is of type IO (). All a Haskell program does, is executing thi=
s=20
I/O operation and this way all your I/O actions can finally get executed.

Yours, Wolfgang