Sh4pe
Sh4pe

Reputation: 1876

Looping over Monads in Haskell

I am really new to Haskell, so this might be a stupid question. I have a function

foo :: Int -> IO ()

whose result will print some useful information. Now I want to do this:

do
    foo 0
    foo 1
    foo 0
    foo 2
    foo 3

How can I write this as a loop? My problem is to 'concatenate' the Monads, which is done automatically by the do statement...

Thank you for your help!

Upvotes: 8

Views: 449

Answers (2)

Chris Taylor
Chris Taylor

Reputation: 47392

You want the mapM_ combinator, which maps a function returning a monadic value over a list, and uses the bind operator to sequence the results:

>> let foo n = putStrLn (show n ++ "!")
>> mapM_ foo [0,1,0,2,3]
0!
1!
0!
2!
3!

Sometimes people like to use the flipped version

for :: Monad m => [a] -> (a -> m b) -> m ()
for = flip mapM_

which looks more like imperative code:

>> for [1..5] $ \n ->
     putStrLn ("Number: " ++ show n)
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5

Note that a combinator called forM_ is defined in Control.Monad and does exactly the same thing as the combinator I've called for.

Upvotes: 12

gspr
gspr

Reputation: 11227

mapM_ foo [0,1,0,2,3] will do the trick.

What's perhaps more important is "how does one figure that out?" Hoogle is a wonderful tool. You want to apply a function with signature Int -> IO () to a bunch of Ints to get a new IO action. The thing you're looking for will thus have signature (Int -> IO ()) -> [Int] -> IO (), so we go and ask Hoogle for functions with that signature. The second result is mapM_, whose signature is

Monad m => (a -> m b) -> [a] -> m ()

Right, so mapM_ in fact works with any monad (not just IO) and any type (not just Int). When you think about it, that's not surprising at all.

Upvotes: 14

Related Questions