Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

user-defined printer in OCaml

printf, fprintf, etc. : all accept the %a conversion.

The manual says for %a:

"user-defined printer. Takes two arguments and apply the first one to outchan (the current output channel) and to the second argument. The first argument must therefore have type out_channel -> 'b -> unit and the second 'b. The output produced by the function is therefore inserted in the output of fprintf at the current point."

I can't understand what a user-defined printer is for, and how you would implement and use it. Can someone explain the motivation and maybe provide an example?

For example, when you want to, say, print a complex data-structure, why is it not possible to just print the data-structure with a custom function directly to a string or to output?

like image 277
ndbd Avatar asked Jul 06 '11 08:07

ndbd


3 Answers

What do you mean by "just print the complex data-structure"? You can do that once you defined a function converting your data-structure to string. It's also possible to "dump" the data-structure with a "default representation" (see, http://caml.inria.fr/cgi-bin/hump.en.cgi?sort=0&browse=139) but that's more for debugging than anything else.

Having said that; a very simple example for %a:

type ty = A | B

let ty_to_string = function
  | A -> "A"
  | B -> "B"

let print_ty chan v = output_string chan (ty_to_string v)

let _ = Printf.printf "%a" print_ty A
like image 73
akoprowski Avatar answered Sep 23 '22 01:09

akoprowski


If you have a function ty -> string you can use it with "%s" to print your data, so I think in realistic cases you can "just print your data structure". It might be a stylistic choice to use "%a" instead. It does seem more consistent in some ways.

On a 32-bit system, strings are limited to around 16MB in length. So you could imagine a case where the "%a" would work while "%s" would fail: if the intermediate string is longer than this. I've never had this come up in practice, though. I just use "%s", myself.

like image 22
Jeffrey Scofield Avatar answered Sep 27 '22 01:09

Jeffrey Scofield


Using %a allows the printing to go directly to the output channel, as opposed to "%s" and stringifying the value you want to print and then printing it.

The distinction seems to be entirely a matter of efficiency - why allocate a potentially large string (or use a buffer, with its exponential resizing and copying) when it's both possible and reasonable to send the serialized data directly to the output channel? Jeffrey points out quite correctly that very large serializations could fail on 32-bit systems because of string length.

I use %a often in my code, using Batteries' composable printing functions to build custom printers for my values:

let range_print oc r = 
  let print_one oc (a,b) = fprintf oc "%d:%d" a b in
  List.print ~first:"" ~last:"" ~sep:"," print_one oc r
let print_rule print_pred print_dec oc r = 
  fprintf oc "%a,%a" print_pred r.pred print_dec r.dec
let print_item oc x = print_rule range_print Int.print oc x in
...
printf "IN : %a\nOUT: %a\n" print_item a print_item b;
like image 37
Thelema Avatar answered Sep 23 '22 01:09

Thelema