I also blog frequently on the Yesod Web Framework blog, as well as the FP Complete blog.

This is a short follow-up to my
blog post about mapM_ and Maybe. Roman
Cheplyaka started a discussion on that
post, and ultimately we came up with the following implementation of
`mapM_`

which works for all `Foldable`

s and avoids the
non-tail-recursive case for `Maybe`

as desired:

```
mapM_ :: (Applicative m, Foldable f) => (a -> m ()) -> f a -> m ()
mapM_ f a =
go (toList a)
where
go [] = pure ()
go [x] = f x -- here's the magic
go (x:xs) = f x *> go xs
```

Why is this useful? If you implement `mapM_`

directly in terms of
`foldr`

or `foldMap`

, there is no way to tell that you are currently
looking at the last element in the structure, and therefore will
always end up with the equivalent of `f x *> pure ()`

in your expanded
code. By contrast, with explicit pattern matching on the list-ified
version, we can easily pattern match with `go [x]`

and avoid ```
*> pure
()
```

bit, thereby making tail recursion possible.

Some interesting things to note:

- Using
`() <$ f x`

instead of`f x *> pure ()`

or`f x >> return ()`

seemed to make no difference for tail recursion purposes. - As a result of that, we still need to have the
`()`

-specialized type signature I describe in the previous blog post, there doesn't seem to be a way around that. - As you can see from the benchmark which I
unceremoniously ripped off from Roman,
there do not appear to be cases where this version has more memory
residency than
`mapM_`

from`base`

. Roman had raised the concern that the intermediate list may involve extra allocations, though it appears that GHC is smart enough to avoid them.

Here are the results. Notice the significantly higher residency
numbers for `base`

:

```
5000 roman 36,064 bytes
5000 michael 36,064 bytes
5000 base 36,064 bytes
50000 roman 36,064 bytes
50000 michael 36,064 bytes
50000 base 133,200 bytes
500000 roman 44,384 bytes
500000 michael 44,384 bytes
500000 base 2,354,216 bytes
5000000 roman 44,384 bytes
5000000 michael 44,384 bytes
5000000 base 38,235,176 bytes
```

My takeaway from all of this: it's probably too late to change the
type signature of `mapM_`

and `forM_`

in `base`

, but this alternative
implementation is a
good fit for mono-traversable. Perhaps
there are some rewrite rules that could be applied in `base`

to get
the benefits of this implementation as well.

Completely tangential, but: as long as I'm linking to pull requests based on blog posts, I've put together a PR for classy-prelude and conduit-combinators that gets rid of generalized I/O operations, based on my readFile blog post.

- The Worst Function in Conduit
*May 7, 2017* - Stackage's no-revisions (experimental) field
*April 27, 2017* - Haskell Success Stories
*April 24, 2017* - Generalizing Type Signatures
*April 20, 2017* - Enough with Backwards Compatibility
*5 Nissan, 5777* - Better Exception Messages
*February 16, 2017* - Hackage Security and Stack
*February 14, 2017* - Stackage design choices: making Haskell curated package sets
*January 23, 2017* - Follow up on mapM_
*January 19, 2017* - safe-prelude: a thought experiment
*January 16, 2017* - Foldable.mapM_, Maybe, and recursive functions
*January 10, 2017* - Conflicting Module Names
*January 5, 2017* - Functors, Applicatives, and Monads
*January 3, 2017* - Beware of readFile
*December 22, 2016* - Call for new Stackage Curator
*December 19, 2016* - An extra benefit of open sourcing
*December 13, 2016* - Haskell Documentation, 2016 Update
*November 28, 2016* - Haskell for Dummies
*November 23, 2016* - Spreading the Gospel of Haskell
*November 22, 2016* - Haskell's Missing Concurrency Basics
*November 16, 2016* - Designing APIs for Extensibility
*November 3, 2016* - New Conduit Tutorial
*October 13, 2016* - Respect
*September 29, 2016* - Proposed conduit reskin
*September 23, 2016* - Monads are like Lannisters
*September 12, 2016* - Using AppVeyor for Haskell+Windows CI
*August 31, 2016* - Restarting this blog
*August 24, 2016* - XSLT Rant Explained
*April 9, 2012* - Open Letter to XSLT Fans
*April 5, 2012* - Dysfunctional Programming: FindMimeFromData
*March 22, 2012* - First Post
*January 31, 2012*