ppx
also: ppx rewriter, extension point, deriver, ppxlib, deriving
A preprocessor that rewrites the OCaml syntax tree between parsing and type checking. Derivers generate code from type declarations tagged [@@deriving name]; extension points expand nodes written [%name payload]. Neither sees any types.
OCaml's syntax has two hooks reserved for preprocessors. Attributes, [@...] and [@@...], annotate existing syntax and are ignored by the compiler if nothing handles them. Extension nodes, [%...], stand for syntax that must be expanded, and the compiler rejects any that are left.
Using a deriver from dune: preprocess names the rewriters, and the attribute says which types to derive for.
(library(name shapes)(preprocess (pps ppx_deriving.show)))type shape = Circle of float | Rect of float * float[@@deriving show](* generates pp_shape and show_shape *)
ppxlib is the library rewriters are written against. It gives a stable view of the AST across compiler versions, builds AST fragments from quotations like [%expr ...], and runs every registered rewriter in one pass over each file.
A rewriter runs before type checking, so it knows the shape of a type declaration but not what any expression's type is. Code that needs types either takes them from the declaration it was attached to, or is written so that the type checker resolves what the ppx could not.
see also
- Type-directed disambiguationOCaml's rule, since 4.01, for deciding which record type a field label or which variant a constructor belongs to when several in scope share the name: if the expected type is already known at that point, it decides; otherwise the most recently defined type with that name wins.
- Deriving strategyAn explicit choice of how a derived instance is produced: stock (compiler-generated), newtype (reuse the underlying type's instance by coercion), anyclass (take the class's own defaults), or via (borrow the instance of a representationally equal type). Naming the strategy removes the ambiguity that arises when more than one is applicable.
- LR parsingBottom-up parsing driven by a table of states: the parser shifts tokens onto a stack, and reduces the top of the stack by a grammar rule when the next token says to. A grammar for which the table cannot be built without a choice has conflicts, reported as shift/reduce or reduce/reduce.
referenced by
- Lenses and Prisms in OCamlAn optics library whose kinds are a phantom polymorphic variant, and a ppx that derives lenses from records, prisms from variants, and both from field paths and patterns.
- ppx in PracticeFrom ppxlib boilerplate to a working [%where] extension: AST traversal, deriver registration, error nodes, and reading generated code.
further reading
- The OCaml manual, “Attributes” and “Extension nodes”.
- The ppxlib manual.