Building a record works like this: let's say you have a record 'r which has n fields of types 'f1, ..., 'fn.
First you call record ~create, supplying a create function of type 'f1 -> ... -> 'fn -> 'r. In most cases, your create function can delegate to Fields.create as in the usage example below.
This gives you a ('r, 'f1 -> ... -> 'fn -> 'r) Record_builder.t. The first type parameter 'r is a phantom type which exists only for additional type safety.
You turn this into a ('r, 'r) Record_builder.t by applying the <.*> operator once for each field, in order. First, you call it with a ('r, 'f1) Record_field.t to get a ('r, 'f2 -> .. -> 'fn -> 'r) Record_builder.t, then with a ('r, 'f2) Record_field.t to get a ('r, 'f3 -> ... -> 'fn -> 'r) Record_builder.t, and so on until you end up with a ('r, 'r) Record_builder.t. You can obtain a Record_field.t using the field function.
Finally you convert your ('r, 'r) Record_field.t into a 'r Sexp_form.t using finish_record.
Usage example:
module Foo = struct
type t =
{ a : string
; b : int
}
[@@deriving fields, sexp_of]
end
let foo : Foo.t Sexp_form.t =
let open Sexp_form.Primitives in
let module Fields = Foo.Fields in
record ~create:(fun a b -> Fields.create ~a ~b)
<.*> field (string ()) Fields.a
<.*> field int Fields.b
|> finish_record