[Haskell-cafe] so how does one convert an IO a into an a ?

Alastair Reid alastair at reid-consulting-uk.ltd.uk
Thu Jul 8 18:19:39 EDT 2004


> [...]
> So I am still in IO Int land despite having used the >>= in the do syntax.
> [...]

The idea of the IO type is that it marks code that can behave differently each 
time it is called.  For this reason, the IO monad 'infects' everything above 
it in the call chain and, for this reason, the entire program (i.e., 
Main.main) has type IO ().

Rather than trying to extract an Int from the IO monad, you should look at how 
you can use an Int inside the IO monad.  Many programs have a structure like 
this: read a value, compute a result, display the result.  Let's suppose we 
have a function to do each of these three parts:

  readValue :: IO Int
  compute :: Int -> String
  display :: String -> IO ()

Note that reading and displaying have side effects and/or depend on the 
environment so they involve the IO monad.

Since they involve a monad, let's use the do notation.  Obviously, we start by 
reading a value:

  main = do
    x <- readValue
    ....

The next thing to do is to compute a result.  From the types, we know that x 
has type Int so obviously we want something involving 'compute x'.  compute 
is not in the IO monad so we'll use a let binding instead of '<-':

  main = do
    x <- readValue
    let y = compute x
    ...

Now we want to display y.  We know that y has type String so we can apply 
display to it.  'display y' has type 'IO ()' so we use a monadic binding (<-) 
instead of a let binding:

  main = do
    x <- readValue
    let y = compute x
    z <- display y
    ....

Hmmm, what to do with z.  It's type is just () so we don't really need to bind 
it to anything so let's drop the binding to get the complete program:

  main = do
    x <- readValue
    let y = compute x
    display y

Let me summarise what went on here:

1) If something has monadic type (i.e., has side effects or depends
   on the environment it is run in), use '<-' to bind the (non-monadic)
   result to a variable.

2) If something is not monadic (i.e., doesn't affect the world and
   doesn't depend on the world), use 'let' to bind the (non-monadic)
   result to a variable.

3) If something has a monadic type but we don't care about the result
   (so, presumably, it has an interesting side effect) you can drop
   the 'blah <-' part.
   [This last rule makes more sense if you use >>= and >> instead of the 
   do notation.  >>= corresponds to binding a result using '<-' while
   >> corresponds to not binding a result.] 

Another way of looking at it is that the IO monad infects the type of 
everything _above_ it in the call chain but doesn't infect the type of things 
_below_ it.  So if you have a pure function like 'compute', you just call it 
from within the IO monad rather than trying to get out of the IO monad, call 
compute and then go back into the IO monad when you want to display the 
result.

I hope this helps,
--
Alastair Reid


More information about the Haskell-Cafe mailing list