or: Very quickly printing a thing to see what it is
- Looking with utop
- Looking with ocamldebug
- Creating a printer for printf
- Generate a printer with ppx_deriving
Looking with utop
Suppose you've picked up OCaml for Advent of Code or similar and are solving a puzzle while learning the language and your desire to simply know what some data looks like is getting frustrated by OCaml not just letting you dbg!(the_thing).
In a case like this, the first thing to consider is moving your workflow into utop.
(* day11.ml *)
type day11 = Neither | Dac | Fft | Both
utop # #use "day11.ml";; type day11 = Neither | Dac | Fft | Both utop # Neither;; - : day11 = Neither
Interactively, you have more of a default printer. Nested lists, arrays, records, it all prints. Control elision with #print_length and #print_depth. utop is also a nice environment especially for input history and completion - if you forget #print_length you can always enter # and hit tab and rediscover it.
Some things still won't print because the interactive environment doesn't have type information for it:
# cache;; - : (string, int) Hashtbl.t = <abstr>
But again in utop you can find something useful under that module to show you what's going on:
# Hashtbl.to_seq cache |> List.of_seq;;
- : (string * int) list = [("alice", 0); ("bob", 0)]
utop is neither an APL workspace nor an Erlang eshell, but it's not bad if you can work with bytecode performance. Just #use "day11.ml" as needed. If you're doing everything in dune, dune utop loads (only) your libraries.
Looking with ocamldebug
(* o8.ml *)
let rec loop s =
print_endline s;
loop (s ^ ".")
let () = loop ":"
$ ocamlc -g o8.ml
$ ocamldebug ./a.out
OCaml Debugger version 5.4.0
(ocd) run
(* a lot of output, interrupted with ctrl-c *)........................................
Interrupted.
Time: 70000 - pc: 3:8500 - module Stdlib
369 <|b|>unsafe_output_string oc s 0 (string_length s)
(ocd) p s
s: string =
":.............(* string length 5000; truncated *)
(ocd) bt
Backtrace:
#0 Stdlib stdlib.ml:369:3
#1 Stdlib stdlib.ml:492:25
#2 O8 o8.ml:2:18
#3 O8 o8.ml:5:10
(ocd) up 2
#2 O8 o8.ml:2:18
2 print_endline s<|a|>;
(ocd) p String.length s
>> Fatal error: Cannot find address for: Stdlib.String.length
Fatal error: exception Misc.Fatal_error
String.length is inlined and not available to the debugger, which also requires bytecode compilation. I give it some slack because it's capable of time travel, but coming from other languages it's disappointing.
When compiled to native code you can use gdb, but apart from correct OCaml source listings it's still not pleasant and couldn't even p s from the previous example.
Creating a printer for printf
Most obviously you could write a string_of_type or a Module.to_string, but a convenient alternative is to define a pretty-printer:
open Printf
let pp_day11 fmt = function
| Neither -> fprintf fmt "Looking for dac or fft"
| Dac -> fprintf fmt "Found dac, looking for fft"
| Fft -> fprintf fmt "Found fft, looking for dac"
| Both -> fprintf fmt "Got dac and fft, looking for out"
which can be passed, along with your value, to %a in a format string:
# Printf.printf "state: %a (after %d moves)" pp_day11 Both moves;; state: Got dac and fft, looking for out (after 20 moves)
The same code works if you open Format instead, which has other conveniences for e.g. nicely-formatted lists:
# Format.printf "[%a]" (Format.pp_print_list ~pp_sep:(fun fmt () -> Format.fprintf fmt ", ") Format.pp_print_int) [1;2;3;4];; [1, 2, 3, 4]- : unit = ()
Generate a printer with ppx_deriving
With a fresh dune project ( and after opam install dune ppx_deriving):
(* lib/day11.ml *)
type day11 = Neither | Dac | Fft | Both [@@deriving show]
; lib/dune (library (name o8_deriving) (preprocess (pps ppx_deriving.show)))
you get reasonable default printers:
$ dune utop utop # open O8_deriving.Day11;; utop # show_day11 Neither;; - : string = "Day11.Neither" utop # Format.printf "state: %a\n" pp_day11 Neither;; state: Day11.Neither
Although it's not so convenient as to synthesize printers for Stdlib types:
Error: Unbound value Hashtbl.pp