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
nucleotidetype 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.ocamlinitso 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_nucleotidefunction 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_equalfunction on component types,derivingcan deriveequalfor 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
Corelibraries are designed to play well as they haveList.equal,Queue.equalbuilt 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
tof a module, the name looked for isMy_module.equalinstead ofequal_t
Some other useful @@deriving type accessor extensions in ppx_jane
sexpgenerates 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 likeequalexcept it makes acomparefunction 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
Coredoes not have libraries for dealing with JSON unfortunately.sexpis 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_yojsonwhich works with theyojsonlibrary to do something like whatderiving sexpabove did. - Aside:
ppx_deriving_yojsonis not compatible withppx_janeso if you want to derive equality and comparisons along withyojsonyou need to use#require "ppx_deriving.eq";; / [@@deriving eq]and#require "ppx_deriving.ord";; / [@@deriving ord]in place of theequal/comparederiving inppx_jane.ppx_deriving show, which prints variant type data, is also not compatible withppx_jane.