[Haskell-cafe] foreach

Tim Newsham newsham at lava.net
Wed Sep 13 14:48:37 EDT 2006

>  foreach l f = mapM_ f l

... rename to forM_ as per previous emails ...

I would like to add to this.  The previous loop runs the code
once independantly for each item in the list.  Sometimes you want
to carry state through the loop:

     v = init
     foreach x list do
         v = update v

(I know that this can be done with IORefs, but ignoring that for now)
for looping with a carried variable:

     forXM_ init [] f = ()
     forXM_ init (x:xs) f = do
         i' <- f init x
         forXM_ i' xs f

or with a returned value (the carried variable at the end of the loop):

     forXM init [] f = init
     forXM init (x:xs) f = do
         i' <- f init x
         forXM i' xs f

used as:

     forXM_ 0 [1,2,3,4] (\x n ->
         putStrLn $ unwords [show x, " -> ", show n]
         return $ x + n

looping a fixed number of times when the loop index is not needed:

     loopM_ n f = forM_ [1..n] (\n -> f)

used as:

     loopM_ 5 (
         print "hello"

with a carried variable (with and without a result):

     loopXM_ i n f = forXM_ i [1..n] (\x n -> f x)
     loopXM  i n f = forXM  i [1..n] (\x n -> f x)

(is this related to foldM_?) used as:

     loopXM_ 1 5 (\x ->
         print x
         return (x * x + x)

do..while loop with a carried variable:

     untilXM_ i p f = do
         i' <- f i
         when (p i') (untilXM_ f i')
     untilXM i p f = do
         i' <- f i
         if (p i') then (untilXM_ f i') else (return i')

used as:

     untilXM_ 1 (< 100) (\x ->
         print x
         return (x * x + x)

Some of these also make sense in pure-functional code (obviously not the 
ones with no return values).  For example the following iteration with a 
loop-carried variable:

     s = 0
     foreach n [1,2,3,4]
         s += n
     return s

is "foldl (+) 0 [1,2,3,4]" and we can mimic the syntax by reordering
the arguments:

     forX i l f = foldl f i l

     forX 0 [1,2,3,4] (\x n ->
         x + n

Obviously many of these examples can be rewritten easily without using
these constructs, but as was pointed out earlier in the thread, larger,
more complicated programs are more difficult to juggle around.  These
constructs are fairly easy to understand, at least for people coming
from an imperative background...

I recently rewrote a small (a few hundred lines of code) imperative
program in haskell using these constructs without changing the structure
of the code very much.  Maybe I'm missing the point somewhat (I'm still
learning haskell) by trying to mimick imperative constructs in haskell,
but the translation was much simpler (and mechanical) this way...

Tim Newsham

More information about the Haskell-Cafe mailing list