liboleg-0.2: A collection of Oleg Kiselyov's Haskell modules (2009-2008)



Safe and generic printf/scanf with C-like format string

We implement printf that takes a C-like format string and the variable number of other arguments. Unlike C of Haskell's printf, ours is total: if the types or the number of the other arguments, the values to format, does not match the format string, a type error is reported at compile time. To the familiar format descriptors %s and %d we add %a to format any showable value. The latter is like the format descriptor ~a of Common Lisp. Likewise, we build scanf that takes a C-like format string and the consumer function with the variable number of arguments. The types and the number of the arguments must match the format string; a type error is reported otherwise.

Our approach is a variation of the safe printf and scanf described elsewhere on this page. We use Template Haskell to translate the format string to a phrase in the DSL of format descriptors. We use the final approach to embed that DSL into Haskell.

Unlike the safe printf explained in the Template Haskell documentation, in our implementation, format descriptors are first class. They can be built incrementally. The same descriptor can be used both for printing and for parsing. Our printf and scanf are user-extensible: library users can write functions to direct format output to any suitable data sink, or to read parsed data from any suitable data source such as string or a file. Finally, what is formatted can be parsed back using the same format descriptor.

Here are some of the tests from the test collection referenced below. The evaluation result is given in the comments below each binding. Example t31 shows that format descriptors are indeed first-class. The definition t32, when uncommented, raises the shown type error because the format descriptor does not match the type of the corresponding argument.


sscanf :: String -> FSc a b -> b -> Maybe aSource

(^) :: FormattingSpec repr => repr b c -> repr a b -> repr a cSource