[ANN] Sexp_decode: monadic decoders of S-expressions

It is my pleasure to release the Sexp_decode library.

Sexp_decode is a library of monadic combinators for decoding S-expressions (as defined in the Csexp library) into structured data. S-expressions are a simple serialisation format for data exchange, that is (in particular) used in dune. Decoders are a form of parsers for S-expressions.

Repository: https://gitlab.inria.fr/bmontagu/sexp_decode

Sexp_decode is available on opam.
You can install it by typing opam install sexp_decode


The purpose of the library is to help the translation of S-expressions into structured data.

For example, you may want to transform an address book encoded as an S-expression into structured data, that is easier to process.

Let’s assume your address book looks like the following:

open Sexp_decode

let address_book : sexp =
        Atom "entry";
        List [ Atom "name"; Atom "John Doe" ];
        List [ Atom "country"; Atom "New Zealand" ];
        Atom "entry";
        List [ Atom "name"; Atom "Mary Poppins" ];
        List [ Atom "email"; Atom "umbrella@imaginary-domain.uk" ];
        Atom "entry";
        List [ Atom "name"; Atom "Groot" ];
        List [ Atom "country"; Atom "Groot" ];

A representation as an OCaml value that is probably easier to work with, is by using the following entry type:

type entry =
  { name : string; country : string option; email : string option }

type address_book = entry list

It is easy to define decoders that produce values of types entry and address_book :

let entry_decoder : entry decoder =
field "entry"
@@ let* name = field "name" atom in
   let* country = maybe @@ field "country" atom in
   let+ email = maybe @@ field "email" atom in
   { name; country; email }

let address_book_decoder : address_book decoder = list entry_decoder

Then, you can execute the run function, that has type 'a decoder -> sexp -> 'a option . It produces the following result on our address_book example:

let result = run address_book_decoder address_book
(* result =
      [{name = "John Doe"; country = Some "New Zealand"; email = None};
       {name = "Mary Poppins"; country = None;
        email = Some "umbrella@imaginary-domain.uk"};
       {name = "Groot"; country = Some "Groot"; email = None}]

In addition to the field , maybe , atom and list decoders, the Sexp_decode library provides combinators to build compound decoders from basic ones, and compose them together. In particular, decoders for variants and records are provided.

For example, with the fields combinator, you could define entry_decoder as follows:

let entry_decoder_alt : entry decoder =
  field "entry"
  @@ fields
       ~default:{ name = ""; country = None; email = None }
         ("name", atom >>| fun name entry -> { entry with name });
         ( "country", atom >>| fun country entry -> { entry with country = Some country });
         ("email", atom >>| fun email entry -> { entry with email = Some email });

With this alternative decoder for entries, the fields "name" "country" and "email" might occur in any order, and any number of times.


Thank you for sharing this! Out of curiosity, what prompted you to write it vs using ppx_sexp_conv?

1 Like

It is true that ppx_sexp_conv is a great tool, that automates the production of S-expressions from a type definition, and their parsing.
With sexp_decode, you do not benefit from the automation provided by the PPX: you need to write your decoders. With sexp_decode, however, you can not only produce data from S-expressions, but also you can (if this is what you want) compute results (like “how many entries with emails do I have in the address book”) directly from the S-expression, without allocating a data structure to represent the whole address book.
On a personal note, designing this library was a lot of fun!


I know that feeling!:smiley: I “rolled my own” quite a few little utilities as part of my learning the language and its libraries. There’s something really addictive for me with OCaml compared to what I’ve known before.