How to print anything in OCaml
Tags: ocaml
Published: 2022-06-04 18:51:39

---
title: How to print anything in OCaml
published: true
description: How to use the `Fmt` module to pretty-print OCaml values
tags: ocaml
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
---
ONE of the big [benefits](/yawaramin/practical-ocaml-314j) of OCaml is its powerful REPL (also called the toplevel), the interactive command-line utility where you can load modules, type in and execute code, and see its results. The modern REPL, utop, has powerful auto-completion and integration with the build system [dune](https://dune.build/), which enables productive workflows like loading an entire project's libraries in the REPL and interactively exploring them.

However, OCaml's strict typing system has a rather large drawback when using it in this interactive way–for many types, their values cannot be displayed in the REPL. This is because the types don't exist at runtime (when the values are calculated). The REPL only knows how to display a limited set of values. When trying to use and explore libraries interactively, this is quite a barrier.

## Pretty-printing from the REPL

Fortunately, the REPL can be taught how to _pretty-print_ values of any type, using its built-in `#install_printer` command. In the next section we will see how to use that, but first, let's understand what the REPL can display by itself. In the following session, the `#` character is the REPL prompt, and lines starting with `-` are output:

```
# false;;
- : bool = false
# 1;;
- : int = 1
# 'a';;
- : char = 'a'
# "hello";;
- : string = "hello"
# Unix.gmtime (Unix.time ());;
- : Unix.tm =
{Unix.tm_sec = 47; tm_min = 20; tm_hour = 18; tm_mday = 4; tm_mon = 5;
 tm_year = 122; tm_wday = 6; tm_yday = 154; tm_isdst = false}
# Unix.O_WRONLY;;
- : Unix.open_flag = Unix.O_WRONLY
```

Above we see that the REPL can display core built-in types, record types, and variant types. It has knowledge of how these types are defined and how to traverse their (sometimes complex) structures.

However, let's try displaying some custom types:

```
# let inc = open_in "README.md";;
val inc : in_channel = <abstr>
# close_in inc;;
- : unit = ()
# object
  method x = 1
end;;
- : < x : int > = <obj>
```

The REPL doesn't know how to display the value of the abstract type `in_channel`, so it shows simply `<abstr>`. And it doesn't know how to display the value of an object, so it shows `<obj>`. In practice, these are the types we will need to teach it about.

## Installing a printer

Earlier we mentioned the `#install_printer` command, it works like this:

```
# #install_printer pp;;
```

Where `pp` is a pretty-printer function of a specific type, `pp : Format.formatter -> 'a -> unit`. Where do we get `pp` from? Usually, we define it by hand for custom types. Defining these functions for custom types can be a bit tedious; fortunately, the [fmt](https://erratique.ch/software/fmt/doc/Fmt/index.html) library helps us by abstracting away much of the tedium.

The `fmt` library is popular in the ecosystem and chances are you already have it installed in your current opam switch. Let's check:

```
$ opam list | grep fmt
fmt                      0.9.0        OCaml Format pretty-printer combinators
```

## Using `fmt`

Now, let's try using `fmt` to create a pretty-printer for a custom type. For the sake of convenience, we will try it for a simple object type:

```
# type t = < x : int >;;
```

First, we define the type we will test with. The specific type is not important; any type would do. The point is to show that we are creating a pretty-printer for a type that the REPL normally cannot display.

```
# #require "fmt";;
# let pp_t : t Fmt.t =
  let open Fmt in
  record [
    field "x" (fun t -> t#x) int;
  ];;
```

Then, we load the `fmt` library and define the pretty-printer. The [naming convention](https://erratique.ch/software/fmt/doc/Fmt/index.html#nameconv) for pretty-printers is to call them `pp_TY`, where `TY` is the name of the type that this printer is for. In our case, this is `t`.

We define the printer by calling the `record` function and passing it a list of 'fields'. Each 'field' is a call to the `field` function that provides:

- The name of the field
- How to extract the field from the object
- The type of the field value

You may be wondering, how did we know that we should use the `record` function? Well, strictly speaking, `record` is meant to be used to define pretty-printers for OCaml record types; however, object types are close enough in nature to record types that I feel it's reasonable to use the same function for them.

```
# #install_printer pp_t;;
# object
  method x = 1
end;;
- : < x : int > = x: 1
```

Finally, we install the pretty-printer and enter an object of the required type to evaluate and display. The pretty-printer now displays the value of the object, as a key-value pair.

## How `fmt` works

The above should give you a taste for `fmt`, but to get more comfortable with it, we need to understand its design. `fmt` provides a set of _combinators_ which can be pieced together like Lego blocks, to build pretty-printer functions of any type `t Fmt.t`. It provides building blocks from the simplest ones, e.g.

- `val int : int Fmt.t`: this pretty-printer knows how to display an `int`. We used it above as part of the definition of the `pp_t` above, as the printer for the value of the `x` method
- `val string : string Fmt.t`: this one knows how to display a `string`

To complex combinators which compose together simpler ones, like `record` which we saw above.

## When to use `fmt`

In my opinion the most compelling use case for `fmt` is creating pretty-printers for the REPL, like above. By default, the actual formatting of the values is very simple, suitable for exploratory work. `fmt` provides options to customize the output to a certain extent. However for more complex control and serialization to various formats, in practice you will probably want to use more targeted libraries like a JSON encoder.