Generic format function for a sequence of floats

Suppose one has a value v of type t — think of it as float vectors. I’d like to have a way to print it (with Printf and Format) specifying a format for the floats it contains. I’d like to use somethink like the following style:

Printf.printf "%s = %a\n" name pr ("%.10f", v);
Printf.sprintf "%s = %a\n" name pr ("%.10f", v);
Format.printf "%s = %a\n" name pr ("%.10f", v);

Is it possible to have the same function pr in all cases? Since it doesn’t seem to be the case, I defined a function:

val fmt : (float -> 'b, 'a, 'b) format -> (t -> 'c, 'd, 'e, 'c) format4

so I can write for the format

# "%s = " ^^ fmt "%.10f" ^^ "\n";;
 - : (string -> I.t -> '_a, '_b, '_c, '_c, '_c, '_a) format6

and it works in all cases. However,

printf ("%s = " ^^ fmt "%.10f" ^^ "\n") name v

is not the nicest IMO because the format string is hard to read. Would something like this be possible

printf "%s = %m\n" name (fmt "%.10f") v

with a new flag, say, %m?

I don’t know if it is what you want, but I just tried the following and it seems to work:

let pp fmt (format, float) = Format.fprintf fmt format float;;
val pp : Format.formatter -> ('a -> 'b, Format.formatter, unit) format * 'a -> 'b = <fun>   

Format.printf "%s = %a\n" "abc" pp ("%.10f", 1.);;
abc = 1.0000000000                                                                                                                                                                                                           
- : unit = ()

Format.printf "%s = %a\n" "abc" pp ("%s", "xyz");;
abc = xyz                                                                                                                                                                                                           
- : unit = ()

But then you need one for Format.sprintf, another one for Printf.fprintf and yet another one for Printf.sprintf. I’d like to have only one — after all, for a given module, they will all do basically the same. Moreover, notice that I do not want to use the format directly but to print each element in a structure such as, say,

Format.printf "%s = %a" "a" pp ("%g", [| 1.; 2. |]);;
a = ⟨1, 2⟩
Format.printf "%s = %a" "a" pp ("%.1f", [| 1.; 2. |]);;
a = ⟨1.0, 2.0⟩

Not really: either Fomat.asprintf or printing in a formatter created from a buffer with Format.formatter_of_buffer will replace favourably Format.sprintf. At most, you would need one version for Printf and Format; but Format features is a superset of Printf features.

This does not change much, using Fmt combinators, this would be

let pp_array ppf (elt_fmt,a) = let open Fmt in
  let elt ppf x = pf ppf elt_fmt x in
  pf ppf "@[[| %a |]@]" (array ~sep:(always ";@ ") elt) a
;; pp_array stdout ("%g", [|1.;2.;3.|]);;
  [| 1; 2; 3 |]- : unit = ()

But this is for a library. I do not want to force my users to choose Format. I would like them to be able to use Printf.(s|f)printf if they like — or anything else that uses format strings for that matter.

I don’t see the harm in forcing your users to use the more featureful Format.

Moroever, it is generally not possible to use the same printing functions (or format strings) for Printf and Format, since Printf cannot recognize Format boxes, break hints or semantic tags … Consequently, writing two functions is a price to pay for supporting Printf-based printing functions.

Similarly, the idea of writing code for hypothetic future libraries that would consume format strings directly (which are currently quite coupled to Scanf/Printf/Format) sounds like premature API optimisations to me.