Skip to content
A ppx rewriter that provides pattern matching on abstract types by transforming patterns into views/expressions.
Branch: master
Clone or download
diml Merge pull request #7 from rgrinberg/fix-tests
Cleanup tests and port to dune
Latest commit 1218528 Oct 8, 2018
Type Name Latest commit message Commit time
Failed to load latest commit information.
ast405 Update AST from 4.04 to 4.05. Mar 1, 2018
common Port to dune Oct 5, 2018
generator Port to dune Oct 5, 2018
parsetree-lib Port to dune Oct 5, 2018
pkg Setup topkg Aug 18, 2017
runtime-lib Port to dune Oct 5, 2018
.gitignore Added .gitignore Aug 18, 2017 Update change log. Aug 21, 2017
Makefile Merge branch 'master' of Oct 24, 2017
dune-project Port to dune Oct 5, 2018
ppx_view.descr Fix name of descr file Aug 18, 2017
ppx_view.opam Port to dune Oct 5, 2018

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__
    [ (constr Viewlib.View.__)
       (fun (Viewlib.View.Var_snoc (Viewlib.View.Var_nil, var)) -> var)]

where __ is used to capture the variable.


ppx_view applies the following mapping:

  • a literal constant c of type typ is mapped to View.typ c;

  • an interval pattern c1..c2 is mapped to View.interval e1 e2 where ci is mapped to ei;

  • 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 to lbl1'match e1 (lbl2'match e2 ...) where pi is mapped to ei;

  • a constructor pattern C (p1, ..., pn) is mapped to c e1 ... en where pi is mapped to ei, except for constructors from the core library:

    • Some is mapped to View.some;
    • None is mapped to View.none;
    • :: is mapped to View.cons;
    • [] is mapped to View.nil;
    • () is mapped to View.unit;
    • true is mapped to View.true_;
    • false is mapped to View.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 ->
  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 ->
  { super with expr; pat; }

Not patterns

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.

You can’t perform that action at this time.