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 -> varthe 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.tTechnically, 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)]
exprwhere __ is used to capture the variable.
ppx_view applies the following mapping:
-
a literal constant
cof typetypis mapped toView.typ c; -
an interval pattern
c1..c2is mapped toView.interval e1 e2whereciis 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 ...)wherepiis mapped toei; -
a constructor pattern
C (p1, ..., pn)is mapped toc e1 ... enwherepiis mapped toei, except for constructors from the core library:Someis mapped toView.some;Noneis mapped toView.none;::is mapped toView.cons;[]is mapped toView.nil;()is mapped toView.unit;trueis mapped toView.true_;falseis 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
| _ -> falseThe 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.noneThe 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.