Adding an ignore function to Control.Monad

Gwern Branwen gwern0 at
Wed Jun 10 12:53:41 EDT 2009

Hash: SHA512

So while writing my wp-archivebot, I ran into the issue that forkIO
requires IO () but returns IO ThreadId, and that many useful IO
functions will return IO a instead of IO ().

This forces some awkward contortions. Suppose I want to ping the
WebCite website at a particular address, and this request makes
WebCite archive a URL embedded in that address. Presumably I could
venture into the depths of Network.HTTP to figure out how to ping an
URL without also pulling down the server's HTML, but why do that when
I already have obviously 'openURL :: String -> IO String'?  Much
easier to do something like 'openURL "" ++ foo ++ "other
stuff" '.

But my bot needs to handle quite a few URLs; one at a time, what with
all the waits and timeouts, isn't going to hack it. So for a given
link, I forkIO the openURL request. But of course, forkIO demands IO
(), so I toss in a '>> return ()'. Fair enough.

So I examine the performance, and it's still too slow. Recent Changes
has hundreds of different pages a minute. I'd better fork each page
(and then fork for each link). But wait, all those forkIOs are
returning IO ThreadIds, and my top-level forkIO call demands IO ()...
So another >> return (). At this point, the code is starting to look
pretty silly - something like '...stuff >> return ()) >> return ())'.

So I see the repeated pattern, and by the rule of 3, factor it out to:

- -- | Convenience function. 'forkIO' and 'forM_' demand return types of
'IO ()', but most interesting
- -- IO functions don't return void. So one adds a call to 'return ()';
this just factors it out.
ignore ∷ (Monad m) ⇒  m a →  m ()
ignore x = x >> return ()

Not the most complex convenience function I've ever written, but not
any simpler than, say Control.Monad.forever or for that matter, most
of the stuff in Control.Monad.

I'd think it'd be useful for more than just me. Agda is lousy with
calls to '>> return ()'; and then there's ZMachine, arrayref, whim,
the barracuda packages, binary, bnfc, buddha, bytestring, c2hs, cabal,
chesslibrary, comas, conjure, curl, darcs, darcs-benchmark,
dbus-haskell, ddc, dephd, derive, dhs, drift, easyvision, ehc,
filestore, folkung, geni, geordi, gtk2hs, gnuplot, ginsu, halfs,
happstack, haskeline, hback, hbeat... You get the picture.

I realize the specific name of 'ignore' can be bikeshedded to death,
but it's clear, it's short, Hoogle turns up one other function with
ignore in its name (Distribution.ParseUtils ignoreUnrec), and it's
been independently named 'ignore' by another Haskeller (lilac).

Existing uses of the string 'ignore are rare - it's in a few places as
a variable, cabal and cabal-install and ehc and tar have where
definitions of an ignore, a test for directory defines an ignore and
imports Control.Monad unqualified, fit defines an 'ignore' but doesn't
seem to use it in any module that also imports Control.Monad
unqualified, halipeto defines an ignore but doesn't import
Control.Monad, shim/yi has a let definition of an ignore, yhc has a
where definition of an ignore. And that's about it. One of directory's
tests would break, and the rest might have an additional -Wall

- --
Version: GnuPG v1.4.9 (GNU/Linux)


More information about the Libraries mailing list