Reputation: 1232
It seems that sprintf
does not allow %a
in its format specifier. Is this right? If so, why is it like this, and is there a workaround?
Example. I have a long and complicated datatype:
type t = Foo | Baz
I have a pretty-printer for this type, that sends a string representation of its values to an arbitrary out_channel
:
let pp_t oc = function
| Foo -> fprintf oc "Foo"
| Baz -> fprintf oc "Baz"
If I then write
let _ = printf "%a=%d" pp_t Foo 2
or
let _ = eprintf "%a=%d" pp_t Foo 2
then everything works fine -- the message "Foo=2" ends up on my stdout or my stderr as expected. But if I write
let s = sprintf "%a=%d" pp_t Foo 2
then I get a compilation error, complaining that the argument pp_t
has type out_channel -> t -> unit
but an expression was expected of type unit -> 'a -> string
.
Is it simply not possible to use %a
inside a format specifier for sprintf
?
Upvotes: 1
Views: 1256
Reputation: 35210
Use the Format
module for the pretty printing and the Format.asprintf
function instead of sprintf
for constructing strings (I usually just do open Format
at the beginning of a file).
It is possible to use the %a
conversion specification with sprintf
. However, the required function has a type that is different from what is required for Printf.fprintf
and Printf.printf
.
All pretty-printers (functions that satisfy a type of a value, that is required by the %a
conversion) encode the type of output in their types. So for different families of printf
functions you need to use different pretty-printing functions.
In the case of the Format
module pretty-printers, you need a function of type formatter -> 'a -> unit
. This is the type that is also required by the top-level and the debugger, and it is also a most common type of a pretty-printing function (libraries such as Core, as well as many other libraries, provide pretty-printers that satisfy this signature). The Format
module also provides the asprintf
function, that, unlike the sprintf
function, plays nice with the %a
conversion (i.e., it has the same formatter -> 'a -> unit
type).
In the case of Printf.printf
the type of a pretty-printer should be out_channel -> 'a -> unit
. This type is much less versatile and thus is much less popular than the Format's one. Very few libraries provide one.
Finally, the Printf.sprintf
function requires a printer of type unit -> 'a -> string
. Usually, the only way to get such pretty printer is to write it manually (sometimes using Printf.asprintf
). In your case it would be:
let pp_t () = function
| Foo -> "Foo"
| Baz -> "Baz"
Upvotes: 4
Reputation: 5167
In order to get away with specific datatypes showing up in your formatting functions you should always use the Format
module and devise pretty printers for your types that have the signature val pp_t : Format.formatter -> t -> unit
; this is also the signature the toplevel asks for to install custom printers with #install_printer
for your datatypes.
Equipped with such a function you can then simply use Format.asprintf
with "%a"
.
Upvotes: 2