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