views:

195

answers:

2

Is there a traditional way to map over a function that uses IO? Specifically, I'd like to map over a function that returns a random value of some kind. Using a normal map will result in an output of type ([IO b]), but to unpack the values in the list from IO, I need a something of type (IO [b]). So I wrote...

mapIO :: (a -> IO b) -> [a] -> [b] -> IO [b]
mapIO f [] acc = do return acc
mapIO f (x:xs) acc = do
  new <- f x
  mapIO f xs (new:acc)

... which works fine. But it seems like there ought to be a solution for this built into Haskell. For instance, an example use case:

getPercent :: Int -> IO Bool
getPercent x = do
  y <- getStdRandom (randomR (1,100))
  return $ y < x

mapIO (\f -> getPercent 50) [0..10] []
+16  A: 

The standard way is via:

Control.Monad.mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

which is implemented in terms of sequence:

sequence :: (Monad m) => [m a] -> m [a]
Don Stewart
Thanks! Exactly what I was looking for.
unignorant
by the way, `mapM` is already in Prelude
newacct
+4  A: 

Just to add to Don's answer, hake a look to the mapM_ function as well, which does exactly what mapM does but discards all the results so you get only side-effects.

This is useful if you want the have computations executed (for example IO computations) but are not interested in the result (for example, unlinking files).

And also see forM and forM_.

Cristiano Paris
and mapM_ runs in constant stack space, whereas mapM needs linear stack, similarly for sequence_ vs. sequence.
Simon Marlow
@Simon, thanks for mentioning this. I always look at my mapM's very carefully to make sure either the list is small or to try to refactor to use mapM_.
John