[Haskell-cafe] printf using values more than once

Olaf Klinke olf at aatal-apotheke.de
Thu Jun 3 20:21:32 UTC 2021

Dear Café,

since the topic recently was rendering text, I'd like to ask a related 
design question. I want a version of printf that
- is more type-safe because unused or surplus arguments cause compile-time 
- the formatting instructions can use arguments out of order and more than 
- is polymorphic in the text type being built: Suppose I want to build a 
Pandoc Block but the holes are of type Inline. The format string must 
therefore be able to promote the Block type's constructors to constructors 
with holes.

The closest match I know of is HoleyMonoid [1] but it only provides 
printf-like holes, whereas I need named positional arguments like arg1, 
arg2 etc. that can be repeated. Document templating libraries provide 
these, but all templating libraries I looked at are type-unsafe.

In order to fulfill the second requirement, one could use the free monad 
over a Reader functor, e.g.
     Free ((->) (forall a. Show a => a)) String
with some custom instances. 
Here one can use the nesting levels of the free monad to encode the 
argument position. But it is just as type-unsafe as printf. Funneling 
return type constructors through this works sort of, but it's awkward.

One can achieve type saftey with type classes like printf does:
class Eventually r a where
    eventually :: r -> a
instance Eventually r r where
    eventually = id
instance (Eventually r a) => Eventually r (b -> a) where
    eventually r = const (eventually r)

This class can be used to inject constant Text into any nesting of
(Text -> ... -> Text -> Text)
and argument position is indeed function argument position. But using this 
system often leads to type ambiguity errors. Also, it is so type-safe that 
it is impossible to write a function taking a natural number and returning 
the hole of the corresponding position: Such a function would be 

My current best implementation uses a combination of Eventually and 
HoleyMonoid, but I'm not entirely satisfied.


[1] https://hackage.haskell.org/package/HoleyMonoid

More information about the Haskell-Cafe mailing list