
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) { 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!" do 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. best, leon