Hello,

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

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

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

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

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

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

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

Since putStrLn is equivalent to \content -> putStrLn content, we can achieve 
the same effect by writing
    readFile "test.dat" >>= \content -> putStrLn content.
Using the syntactic sugar of the do notation, we get the equivalent expression
    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 
you have an I/O computation you have to use the >>= operator to make its 
result available to other parts of your program. You cannot make it available 
by conversion. On the top level, every haskell program has a variable called 
main which is of type IO (). All a Haskell program does, is executing this 
I/O operation and this way all your I/O actions can finally get executed.

Yours, Wolfgang
_______________________________________________
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell

Reply via email to