Pre-processor extensions
Pre-Processor eXtensions (PPXs) are macros, they write code for us. They save us from writing boilerplate code, so our code is shorter and (probably) more correct.
Some examples of boilerplate code ppx extensions can produce include
- Print functions (
show
) - Serialization (
sexp
,yojson
) (turning arbitrary OCaml data into strings) - Comparison (
compare
,equal
)
ppx_jane
and deriving
- In the variants lecture, we wrote a
nucleotide
type and derived an equality function for it. - We put
[@@deriving equal]
in the nucleotide example to get an=
on that type “for free”:- In the top loop, type
#require "ppx_jane"
to load it (but its in the course.ocamlinit
so can skip) - In dune, put
(preprocess (pps (ppx_jane)))
as part of the library declaration (needed!).
- In the top loop, type
# type nucleotide = A | C | G | T [@@deriving equal];;
type nucleotide = A | C | G | T
val equal_nucleotide : nucleotide -> nucleotide -> bool = <fun>
- We defined our type and got the
equal_nucleotide
function for free. - Without this macro we would have had to use pattern matching to write our own equality.
Composing deriving equal
- If we have an
xyy_equal
function on component types,deriving
can deriveequal
for a type built from those components. For example equality on lists of nucleotides:
# type n_list = nucleotide list [@@deriving equal];;
type n_list = nucleotide list
val equal_n_list : n_list -> n_list -> bool = <fun>
# equal_n_list [A;A;A] [A;G;A];;
- : bool = false
# type n_queue = nucleotide Queue.t [@@deriving equal];;
type n_queue = nucleotide Core.Queue.t
val equal_n_queue : n_queue -> n_queue -> bool = <fun>
- Notice that the
Core
libraries are designed to play well as they haveList.equal
,Queue.equal
built in- But,
List.equal : ('a -> 'a -> bool) -> 'a list -> 'a list -> bool
– it needs=
on the underlying list data. - This is a bit annoying as you keep having to pass
=
for members to check'
for lists - .. we will eventually see an alternative to this when we learn about functors in the modules lecture.
- But,
- Note that in general for a component type that is the
t
of a module, the name looked for isMy_module.equal
instead ofequal_t
Some other useful @@deriving
type accessor extensions in ppx_jane
sexp
generates S-expression printable representations of types which is handy for displaying data internals- S-expressions are a general data format like JSON or XML, in fact they are the oldest such format
# type nucleotide = A | C | G | T [@@deriving equal, sexp];;
type nucleotide = A | C | G | T
val equal_nucleotide : nucleotide -> nucleotide -> bool = fun
val nucleotide_of_sexp : Sexp.t -> nucleotide = fun
val sexp_of_nucleotide : nucleotide -> Sexp.t = fun
# type n_list = nucleotide list [@@deriving equal, sexp];;
type n_list = nucleotide list
val equal_n_list : n_list -> n_list -> bool = fun
val n_list_of_sexp : Sexp.t -> n_list = fun
val sexp_of_n_list : n_list -> Sexp.t = fun
# sexp_of_n_list [A;G;G];;
- : Sexp.t = (A G G) (* this is the "S-Expression" version of a list.. parens and spaces *)
# n_list_of_sexp (Sexp.of_string "(A G G)");; (* how to convert in the other direction *)
- : n_list = [A; G; G]
[@@deriving compare]
is likeequal
except it makes acompare
function instead ofequal
# type nucleotide = A | C | G | T [@@deriving compare];;
type nucleotide = A | C | G | T
val compare_nucleotide : nucleotide -> nucleotide -> int = fun
# compare_nucleotide A C;;
- : int = -1
# compare_nucleotide C A;;
- : int = 1
Note that existing types such as int
, string
, etc come with Int.compare
, String.compare
, etc, along with the Int.equal
, String.equal
.
JSON format
- JSON is a common standard for data-as-text and may be useful in your project
Core
does not have libraries for dealing with JSON unfortunately.sexp
is the “JSON for Core”- Anywhere you want to use JSON, just settle for S-expressions if you want to also use Core.
- If you need JSON conversion, use
ppx_deriving_yojson
which works with theyojson
library to do something like whatderiving sexp
above did. - Aside:
ppx_deriving_yojson
is not compatible withppx_jane
so if you want to derive equality and comparisons along withyojson
you need to use#require "ppx_deriving.eq";; / [@@deriving eq]
and#require "ppx_deriving.ord";; / [@@deriving ord]
in place of theequal/compare
deriving inppx_jane
.ppx_deriving show
, which prints variant type data, is also not compatible withppx_jane
.