[Haskell-cafe] mapM_ -> Monoid.Monad.map

Dan Doel dan.doel at gmail.com
Fri Jan 23 16:35:41 EST 2009


On Friday 23 January 2009 3:50:18 pm Henning Thielemann wrote:
>   I always considered the monad functions with names ending on '_' a
> concession to the IO monad. Would you need them for any other monad than
> IO? For self-written monads you would certainly use a monoid instead of
> monadic action, all returning (), but IO is a monad. (You could however
> wrap (newtype Output = Output (IO ())) and define a Monoid instance on
> Output.)
>   However our recent Monoid discussion made me think about mapM_,
> sequence_, and friends. I think they could be useful for many monads if
> they would have the type:
>   mapM_ :: (Monoid b) => (a -> m b) -> [a] -> m b
>    I expect that the Monoid instance of () would yield the same efficiency
> as todays mapM_ and it is also safer since it connects the monadic result
> types of the atomic and the sequenced actions. There was a recent
> discussion on the topic:

What is your proposed definition of the new mapM_? For instance:

  mapM_ f = foldr (liftM2 mappend) (return mempty)

Has exactly the same problem as mapM for the situations you'd actually want to 
use it (roughly, it's not monadically 'tail recursive'). You could instead 
have:

  mapM_ f = foldr (>>) (return mempty)

which does have the right behavior, but it doesn't naturally have the type you 
give above, and always returns mempty, which isn't much more useful than 
always returning ().

mapM_ is also useful in ST (perhaps not surprising) and strict State. It 
should be useful in other monads in which >>= implies a strict sequencing of 
evaluation, as it's the difference between:

  nonTail (x:xs) = do a <- foo x
                      b <- nonTail xs -- overflows for long lists
                      return $ f a b

and:

  tail (x:xs) = do foo x
                   tail xs

Even if f doesn't even look at its arguments, it leads to stack overflows for 
long lists in certain monads.

You can, of course, write a 'tail recursive' mapM by using an Endo m 
accumulator (to preseve ordering), and that might work out well enough, but I 
haven't thought much about it. In any case, I have a hard time believing IO is 
the *only* monad where you might want to write a loop purely for its side 
effects, but maybe I'm off the mark.

-- Dan


More information about the Haskell-Cafe mailing list