[Haskell-cafe] Converting IO [XmlTree] to [XmlTree]

Jake McArthur jake.mcarthur at gmail.com
Tue Apr 14 12:11:50 EDT 2009

rodrigo.bonifacio wrote:
> I guess this is a very simple question. How can I convert IO [XmlTree] 
> to just a list of XmlTree?

You can't, unless you use `unsafePeformIO`, as others have already 
pointed out. Yet others have, more "correctly," suggested that you use 
do notation to bind a variable with the type you expect. I want to go 
into a little more detail .I have a spontaneous Monad tutorial to throw 
out, I guess.

How you can convert a value of type `IO [XmlTree]` is probably the wrong 
question. The right question is how you can convert a function of type 
`[XmlTree] -> A` into a function of type `IO [XmlTree] -> IO A`. The 
correct answer has many names:

     fmap, liftA, liftM, (<$>) :: Monad m => (a -> b) -> (m a -> m b)

I will use `fmap` to mean any of the above names. In your case, applying 
fmap to a function foo:

     foo      ::    [XmlTree] ->    A
     fmap foo :: IO [XmlTree] -> IO A

So any time you need to pass an IO value to a pure function, this is one 
way to do it.

Suppose that the function actually returns an IO value, though. Here, we 
will call the function `bar`:

     bar      ::    [XmlTree] ->     IO A
     fmap bar :: IO [XmlTree] -> IO (IO A)

Now we seem to be in a similar situation as before. We have an extra IO 
that we don't want. There is a function for this:

     join :: Monad m => m (m a) -> m a

So, we can use `join` to transform an expression of type `IO (IO a)` to 
an expression of type `IO a`. Putting it all together:

     bar             ::    [XmlTree] ->     IO A
     fmap bar        :: IO [XmlTree] -> IO (IO A)
     join . fmap bar :: IO [XmlTree] ->     IO A

And we now have a sensible function again.

Of course, this is a common pattern, using `join` and `fmap` together, 
so we have yet another function:

     (=<<) :: Monad m => (a -> m b) -> (m a -> m b)

(Note that this has a different argument order than (>>=). I prefer this 
form since it emphasizes that it actually transforms a function.)

So, now we have

     bar       ::    [XmlTree] -> IO A
     (bar =<<) :: IO [XmlTree] -> IO A

Putting it all together, with a function that gives us the `IO [XmlTree]`:

     xmlTree         :: IO [XmlTree]
     bar             ::    [XmlTree] -> IO A
     bar =<< XmlTree ::                 IO A

And that last line is equivalent to this in do notation:

     do tree <- xmlTree
        bar tree

If you have any questions, please do ask. I understand that it can 
appear quite dense to a beginner. I'm thinking about using this approach 
in a blog article, which would have more detail and examples, but I 
would like to be aware of potential stumbling blocks.

- Jake

More information about the Haskell-Cafe mailing list