Format module from the Standard Library

It’s been ages since I worked with Format; the ability to do so with printf-like formats is quite nice. I just tried out Fmt, and following Drup’s hints, got a JSON pretty-printer working, albeit not as perfectly as I’d like. I can’t speak to whether Fmt is a better way to go than easy-format (I need to try out easy-format also) but at least, Drup seems to be correct, that you can write nontrivial formatters this way.

type json =
    JInt of int
  | JQString of string
  | JAssoc of (string * json) list
  | JList of json list
open Fmt

let qstring ppf v = pf ppf "%S" v
let label ppf s = pf ppf "%S: " s

let rec pprec (f, v) ppf = function
  | JInt n -> pf ppf "%a%a" f v int n
  | JQString s -> pf ppf "%a%a" f v qstring s
  | JAssoc l ->
    pf ppf {|@[<hv2>%a{@ %a@]@ }|}
      f v
     Fmt.(list ~sep:comma ppfield) l
  | JList l ->
    pf ppf {|@[<hv2>%a[@ %a@]@ ]|}
      f v Fmt.(list ~sep:comma (pprec (string, ""))) l

and ppfield ppf (k,v) = pprec (label, k) ppf v

let pptop ppf j = pf ppf "@[<hv>%a@]@." (pprec (string, "")) j

let pp j = pptop std_formatter j

let j1 = JAssoc [
    "field", JAssoc[ "number", JInt 123;
                     "text", JQString "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ] ;
    "field2", JList [ JInt 1; JInt 2;  JInt 3;  JInt 4;  JInt 5;  JInt 6;  JInt 7;  JInt 8;  JInt 9;  JInt 10 ]
  ]

with output

# Json.pp Json.j1 ;;
{
  "field": {
    "number": 123,
    "text": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  },
  "field2": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  ]
}

One observation is that even with Fmt (or Format) it’s a long distance between theoretically understanding the meaning of the various boxes and breaks, and actually being able to use them to produce pretty output.

1 Like

Oh hm, and modifying ppfield

and ppfield ppf (k,v) = pf ppf "@[<hv>%a@]" (pprec (label, k)) v

make the output even better.

Looks good, small remarks:

  • Use (Fmt.nop, ()) instead of (string, "").
  • Don’t use %S for quoted strings, it also reinterpret the non-ascii char, following ocaml conventions, just use {|"%s"|} (or something unicode aware).
    # Format.printf "%S" "é" ;;
    "\195\169"
    
  • You might want to use Fmt.text for string values.
1 Like

Very nice. I agree completely that you can’t use “%S” – that’ll escape the string in Ocaml’s fashion, and what you want, is to escape it by the rules of JSON. I had to use (Fmt.nop "") (b/c, well, long story).

Thank you for both your original post (so long ago) and your latest advice.