
Michael Easter
Q1: The web page mentions that normal Haskell functions cannot cause side-
effects, yet later talks about
side-effects with putStrLn. I assume the key point here that IO actions are, by definition, _not_ normal functions?
The way I look at it, a monad is a way of saying, "we'll be carrying along and combining, in some specific manner, values with some extra information attached to them, which most functions don't need to worry about". _How_ it carries along these values and combines them, and the corresponding hidden data, is what makes the particular Monad type to be what it is. Let's call a function an M-action function if its type is (:: a -> M b), for some monad M. Such functions can get chained by M's bind, as in ( (x :: M a) >>= (f :: a -> M b) ) :: M b M can also provide us with special functions that will reveal its hidden information, e.g. (reveal :: (hidden_stuff -> a -> M b) -> a -> M b), so that for a user-defined (g :: hidden_stuff -> a -> M b), (reveal g) can be chained and the function g will get called providing us with the information, if the monad so chooses. For example, Philip Wadler in his paper "The Essence of Functional Programming" gives an example of a "position" monad P which defines its own special function, resetPosition, enabling us to reset its hidden data - in this case, a code position. IO primitives like putStrLn, instead of revealing anything to us, carry along a promise to take notice of the value they are supplied with and to perform an actual real world IO action with them, when called upon to do so by the system. We can create chains of IO-action functions and store them in variables, without them getting executed by the system - and thus without causing any real- world IO action, e.g. x = do {putStrLn "1";return 2} somewhere in your program will just create a definition that will just hold an IO-action function in it. It's just a feature of an interactive loop that when is sees a value of type (IO a) it will not just return it, but will also execute the promised actions held inside it: -- in HUGS -- Prelude> [ do {putStrLn "1";return 2} ] -- a value just gets returned [<<IO action>>] :: [IO Integer] Prelude> do {putStrLn "1";return 2} -- actual IO action performed 1 :: IO Integer -- in GHCi -- Prelude> let x=[do {putStrLn "1"; return 2}] Prelude> head x 1 2 Prelude> head x 1 2 Prelude> :t it it :: Integer The same goes to the special identifier (main :: IO a) that will also get treated in this special way - getting executed on sight. :)