Polymorphic function; a tale of two forM_’s

Tonight I spent over an hour learning that there are two functions called “forM_” in haskell.

The first, which I was already familiar with, lives in Control.Monad. Its purpose is to take a list of values, apply some function to them all to produce a list of actions, and then run these actions one after the other.

The other one, which I did not know about, lives in Data.Foldable. It does exactly the same job, but it works on any “Foldable” data type; ie. those which can be collapsed/folded in some way to a single summary value. Lists are foldable. Maps are also foldable (across their elements, not their keys).

And this was the cause of my perplexification this evening; I had been storing my data in a list, and using forM_ (the monadic one, the only which I knew about!) to process it. At some point, I decided to change to storing my data in a map instead, and ran the compiler to see the errors which would point me to the bits of code I needed to fix up.

Except the compiler happily compiled everything without errors. *confusion*

Much headscratching followed, until eventually I added “-ddump-tc” to get ghc to dump out the results of its typechecking pass. This lead me to the root cause: I had been picking up forM_ from Data.Foldable all along, not Control.Monad. Duh! Everything had been working fine up until then, since Foldable.forM_ and Monad.forM_ operate identically on lists. But the former also works on Data.Maps whereas the latter does not.

I’m sure there’s a moral to this story. I’m just not sure what it is yet.

2 replies on “Polymorphic function; a tale of two forM_’s”

If there is a moral, it may be for the Haskell library designers, and it may involve recognizing that naming strongly implies semantics – one shouldn’t give two things the same name unless they are the same thing, for the relevant contextual value of “same”.

In the context of methods you can call on objects, I think “same” means identical behavior. So having two methods called forM_ – one that works fine on Data.Maps, and another that does not – is a bit misleading, You were misled.

If this is your worst horror story about coding in Haskell, mind, it speaks rather well of the language!

Ah, I had a similar issue earlier. I was looking for forM_ over a Map and found it in the Traversable class… as forM, with no forM_ to be found.

Now I know I should have been looking in Foldable all along…!

Comments are closed.