[Haskell-beginners] Re: clarification on IO

Will Ness will_n48 at yahoo.com
Sat Feb 28 14:13:01 EST 2009

Michael Easter <codetojoy <at> gmail.com> writes:

> 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
  Prelude> head x
  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. :)

More information about the Beginners mailing list