Haskell Problem

Leon Smith lps@po.cwru.edu
Tue, 10 Oct 2000 19:12:05 -0300

What you need to do is write a function that operates on a string that does
what you want it to,  and then use that to write some top-level I/O code.
If you have a function  sortFile :: String -> String, you would write
something like this for main:

main :: IO ()
main = do
    string <- getContents "theFile"
    putStr (sortFile string)

You can treat "string"  as a variable that has type String, not IO String,
which you can use anywhere you want in "main".  Keep in mind, though that
what is going on here is quite different than an assignment statement or
"converting" a IO String to a String.

This is not like the single assignment variables introduced in "where" or
"let" clauses, as we cannot substitute the value "(getContents "theFile")"
for the variable "string" in main.   This would lead to a type error, as
sortFile takes a String argument, not an IO String.

Nor is is it like the assignment statement in imperative programming
languages like C++ and Java for several reasons.   One can represent  "State
Transformers"  using monads, so what the IO monad is a state transformer
that modifies the state of the computer.

int a = 0;
int dirty_inc(int a) {
    return i + a;

int main(int argc, char ** argv) {
    int i = dirty_inc(1);
    printf("%i %i", i, i);

Unlike monads, if you "substitute" dirty_inc(1) for i in main will result in
a legal program, but it isn't really a substitution, because it would modify
the behavior of the program.  Moreover, while we could write

main = do
    message <- return "Hello!"
    message <- return "Goodbye!"
    putStr message

and get "Goodbye!" as output, what really is happening is that you are
introducing two variables with the same name, and we can statically
determine which one we are referring to.  Thus if we write

main = do
    message <- return "Hello!"
        message <- return "Goodbye! "
        putStr message
    putStr message

we will get "Goodbye! Hello!",  as output, not "Goodbye! Goodbye!".

To start to understand what's really going on,  do-notation is just
syntactic sugar for using the (>>=) operator.  Let's rewrite your example to
something that is syntactically equivalent:

main :: IO ()
main = getContents "theFile" >>= (\string -> putStr (sortFile string))

Which we could in turn rewrite as:

main :: IO ()
main = getContents "theFile" >>= output_sort

output_sort :: String -> IO ()
output_sort string = putStr (sortFile string)

What (>>=) does is that it takes the String returned inside of a IO String
value, and gives it to output_sort, which in turn may use that value in any
way it sees fit, *as long as output_sort returns another "IO a" value for
some type a.*    This is why we are not simply converting a IO String to a
String, because in order to use the String value in IO String, we must
produce a new IO monad.   This is summed up in (>>=)'s type, which is  (>>=)
:: IO a -> (a -> IO b) -> IO b, which can then be generalized to any monad
m, so (>>=) :: m a -> (a -> m b) -> m b.