http://www.haskell.org/onlinereport/exps.html#do-expressions IO is Haskell is different from everything else. Functional programs store all their state in their parameter inputs; and they produce identical output for the same input. ...except when they don't. things like Random and I/O run the same functions but do not produce the same answers. For this Haskell has "IO", which is special. "IO" needs to be imported as a package import IO and things that do any IO, any reading or writing have special notation in their type signatures: > :t getLine getLine :: IO String note that IO and String are not separated by an -> in getLine's signature, nor are they bound with (); this is special syntax reserved for IO. special. > :t putStr putStr :: String -> IO () putStr receives a string, does some IO with it and returns nothing useful (). this is special. how do function's get IO in their signatures? with do. having do in your function means it does IO, which adds IO to every function in which you use it. let's see what using do does: > let no_io = undefined > :t no_io no_io :: a no_io takes no arguments and returns a type of undefined nature. it's useless, but it's simple and it's legal. > let io = do x <- getLine; return x > :t io io :: IO String