This project is under construction
For more info, see this blog post.
The ppxlib project provides the basis for the ppx system, which is currently the officially supported method for meta-programming in OCaml. It offers a principled way to generate code at compile time in OCaml projects.
Ppxlib comes with a user manual aimed at both users and authors of ppx rewriters.
This repository was created by merging several older projects. See the history for more details.
A ppx rewriter that provides pattern matching on abstract types by transforming patterns into views/expressions.
ppx_view
transforms the patterns of match
/function
constructs
wrapped inside [%view]
extensions by replacing matches against
constructors with function calls.
For instance, in the following code:
match%view expr with
| Constr var -> var
the constructor Constr
is turned into a call to the function
constr
, which is expected to be a view pattern:
val constr : (string, 'a, 'b) View.t -> (expression, 'a, 'b) View.t
Technically, the above expression is rewritten into:
Viewlib.View.match_ __POS__
[Viewlib.View.case (constr Viewlib.View.__)
(fun (Viewlib.View.Var_snoc (Viewlib.View.Var_nil, var)) -> var)]
expr
where __
is used to capture the variable.
ppx_view
applies the following mapping:
-
a literal constant
c
of typetyp
is mapped toView.typ c
; -
an interval pattern
c1..c2
is mapped toView.interval e1 e2
whereci
is mapped toei
; -
a variable pattern is mapped to
View.__
; -
a catch-all pattern is mapped to
View.drop
; -
a record pattern
{ lbl1 = p1; lbl2 = p2; ... }
is mapped tolbl1'match e1 (lbl2'match e2 ...)
wherepi
is mapped toei
; -
a constructor pattern
C (p1, ..., pn)
is mapped toc e1 ... en
wherepi
is mapped toei
, except for constructors from the core library:Some
is mapped toView.some
;None
is mapped toView.none
;::
is mapped toView.cons
;[]
is mapped toView.nil
;()
is mapped toView.unit
;true
is mapped toView.true_
;false
is mapped toView.false_
.
Note: the following patterns are currently not supported:
- polymorphic variants;
- lazy;
- module unpacking;
- exceptions.
The Parseview
module of the library contains the functions
corresponding to the constructors and records from the Parsetree
module. Such functions use "shortcuts" to xyz_desc
fields, allowing
to directly match constructors:
open Viewast
let is_zero : Parsetree.expression -> true = function%view
| Pexp_constant (Pconst_integer ("0", _)) -> true
| Pexp_ident { txt = Lident "zero"; _ } -> true
| _ -> false
The access to other fields is done through a [@view ...]
annotation:
open Viewast
let is_zero : Parsetree.expression -> true = function%view
| (Pexp_constant (Pconst_integer ("0", _)))[@view { pexp_loc; }] -> true, pexp_loc
| Pexp_ident { txt = Lident "zero"; loc; } -> true, loc
| _ -> false, Location.none
The library also provides an Ast_viewer
module that acts as the counterpart
of the Ast_helper
module. It allows to write patterns very similar to the
corresponding expressions:
open Viewast
let twice_mapper =
let module H = Ast_helper in
let module M = Ast_mapper in
let module V = Ast_viewer in
let super = M.default_mapper in
let expr self e =
match%view super.expr self e with
| V.Exp.Constant (V.Const.String (str, _)) ->
H.Exp.constant (H.Const.string (str ^ str))
| other ->
other
and pat self p =
match%view super.pat self p with
| V.Pat.Constant (V.Const.String (str, _)) ->
H.Pat.constant (H.Const.string (str ^ str))
| other ->
other
in
{ super with expr; pat; }
Following the proposal in MPR#7628,
not pattern are implemented through the Not
constructor, that is
rewritten to View.not
. Not patterns allow to write patterns that
are matched if a subpattern is not matched as in:
match%view expr with
| Pexp_constant (Not (Pconst_integer _ | Pconst_float _)) ->
(* expr is not a number *)
| Pexp_constant (Pconst_integer _) ->
(* expr is an integer *)
| Pexp_constant (Pconst_float _) ->
(* expr is a float *)
Not patterns cannot contain variables.
The major limitations of view patterns are the lack of checks for non-redundancy and exhaustivity.