Riddle me this
September 25th, 2009 by Bryan O'Sullivan
I find Lennart Augustsson's Text.Printf module very handy, but also quite baffling.
Here is one example of my bafflement: suppose I want to print a piece of text if my program's verbosity level is above a certain threshold, but not otherwise. I'd like to hide that detail in a function, such that I could just call something like this:
note cfg "mean is %s (%d iterations)\n" (secs µ) iters
and my note function would Do The Right Thing. The type signature of hPrintf seems to make this impossible to do elegantly, since the number of arguments to note is not clear. Hence a fugly hack:
nullDev :: Handle
nullDev = unsafePerformIO $ openBinaryFile "/dev/null" WriteMode
{-# NOINLINE nullDev #-}
note :: (HPrintfType r) => Config -> String -> r
note cfg msg = if cfgVerbosity cfg > Quiet
then hPrintf stdout msg
else hPrintf nullDev msg
This makes me a sad Irish panda.
The next thing I'd like to be able to do, and over which I am also baffled, is to hide that Config parameter, since it's ubiquitous in my code:
newtype Criterion a = Criterion (ReaderT Config IO a)
deriving (Monad, MonadReader Config, MonadIO)
Most of the time, I can ask for the current Config when I need to. However, when it comes to eliminating this parameter from my note function, I am once again stymied by the type of hPrintf, since there's no way to tell what monad I'm living in.
Help!

this is a terrible hack, but no more of a hack than printf itself:
test :: (HPrintfType r, NoOp r) => Bool -> Handle -> String -> r
test b = if b then hPrintf
else nopr
class NoOp a where
nopr :: a
instance NoOp (IO a) where
nopr = return undefined
instance (NoOp r) => NoOp (a -> r) where
nopr = const $ nopr
don’t have an answer for the second bit at the moment tho.
Don’t forget package ‘xformat’.
I got a solution requiring more extensions but that it looks more general, here’s the paste:
http://hpaste.org/fastcgi/hpaste.fcgi/view?id=9857
The types, especially those inferred like in test, aren’t so pretty but not horrible either.
Saizan: Wow, that’s impressive. Type signatures longer than the code they describe? Eeek!
well, the typeclass system is what’s doing most of the work
[...] little while ago, Bryan O’Sullivan was developing his Criterion benchmark suite, and had trouble with using the Text.Printf module in a monad transformer on top of IO. I thought I knew how to solve this, but my first idea didn’t work — and nor did my [...]