- OCaml 99.3%
- Makefile 0.7%
| src | ||
| tests | ||
| _tags | ||
| descr | ||
| license | ||
| Makefile | ||
| META | ||
| opam | ||
| ppx_listlike.install | ||
| readme.md | ||
Ppx_listlike
List literals for custom types
The main objective of this ppx extension for ocaml is to make possible the use of the list literal syntax for custom types.
##List literals
The simplest use of this syntax extension is to define a type t with two constructors Cons and Nil. For instance, we can define an alternating list
type ('a,'b) t = Nil | Cons of 'a * ('b,'a) t
We can then define a new value of type t by combining an ll extension point with a standard list literal:
let n =[%ll 1; "One one"; 11; "Two one"; 21; "One two one one" ]
Inside the extension point [%ll ..], the sequence of expression e1;e2;.. is rewritten to the expression Cons(e1, Cons(e2, ..., Nil)...). The type of the list-like literal thus depends of the type of the constructors Cons and Nil in scope.
##Pattern matching and [%with_ll ..] extension nodes
It is also possible to use list-like syntax extension inside pattern with [with_ll?..]:
(* map : ('a,'b) t -> ('a->'c) -> ('b->'d) -> ('c,'d) t *)
let rec map f g = function
| [%with_ll? a::q ] -> Cons(f a, map g f q)]
| [%with_ll? [] ] -> []
However, the behavior of the [%with_ll? ..] is slightly different from the behavior of [%ll .. ]. Inside the extension node [%with_ll? ..], all list literals [e1;e2;..] are rewritten to Cons(.., Nil). Similarly all expressions or patterns of the form a::q are transformed to Cons(a,q).
The [%with_ll ..] extension node can be used outside of patterns by removing the trailing ? which marks a pattern extension node. For instance, we could rewrite the previous map function as
(* map : ('a,'b) t -> ('a->'c) -> ('b->'d) -> ('c,'d) t *)
let rec map f g =[%with_ll function
| a::q -> (f a)::(map g f q)
| [] -> []
]
Another solution for this function would be to open the extension point at the
let level using the shortcut syntax for extension node
(* map : ('a,'b) t -> ('a->'c) -> ('b->'d) -> ('c,'d) t *)
let%with_ll rec map f g =function
| a::q -> (f a)::(map g f q)
| [] -> []
If we want to have normal lists nested inside an [%with_ll .. ] extension node, we need to use a standard list [%stdl ..]-extension node:
let%with_ll nested_list = [1234; [%stdl 1;2;3;4 ]; 2; [%stdl 2] ]
##Pattern matching shortcut
For the sake of brevity, the [%with_ll? ..] extension node for pattern matching can be actually written [%ll? .. ]. However, in this case [%ll?..] is semantically only a shortcut for the [%with_ll?..] syntax.
Advanced uses
Inside the compiler, list literals of the form [a;b;c] are rewritten to
:: (a, :: ( b, :: ( c, [] ) ) ). Inside this expression, the constructor :: and [] are special constructors. Moreover, the constructor [] cannot be redefined.
To circumvent this limitation, ppx_listlike adds a mechanism to define list-like constructor rewriter. New rewriters can be defined inside a module by
let%ppx_listlike nl = { cons="Cons_nl"; nil="Nil_nl"; kind=List }
The fields cons and nil define the name of the constructors that will replace the :: and []. The kind field is defined to List for list-like constructor rewriter. Once this nl rewriter defined, list literals inside a [%with_nl ..]
extension point will be replaced the corresponding "Cons_nl .. Nil_nl" construction.
The simplified [%nl .. ] extension node implements a limited version of this rewriting mechanism which transforms an expression sequence e1;e2;.. to the corresponding list literal.
For instance, the ll and stdl extension point used earlier are predefined constructor rewriters corresponding to:
let%ppx_listlike ll = { cons="Cons"; nil="Nil"; kind=List }
and stdl = { cons = "::"; nil="[]"; kind=List }
For obvious reasons, a valid rewriter name can not start with a "with_" prefix. It is also possible to define local rewriters active only inside an expression with a let ... in binding. Moreover, standard syntax for record can be used but the value of the fields needs to be constant literals. For instance
let%ppx_listlike ex = { ll with cons = "Snoc"; nil = "Lin" }
is fine but
let not_a_literal = "Cons"
let%ppx_listlike ex = { ll with cons = not_a_litteral }
is not.
##Avoiding ppx interferences
If the predefined [%ll] and [%stdl] rewriter interfers with some other ppx extensions, the -nostd option can be used to start ppx_listlike with no predefined rewriters. For findlib based build systems, the easiest way to pass this option is to use the ppx_listlike.notsd subpackage (i.e. -pkg ppx_listlike.nostd rather than
-pkg ppx_listlike).
##Extremely experimental features This ppx extension contains also few very experimental features exploring an alternative syntax/semantic for multidimensionnal indices for array-like type (i.e. array, string and bigarray). These features can be activated by first defining an array indices rewriter
let%ppx_listlike mi = { cons="Cons"; nil="Nil"; kind=String_indices }
Then, inside an [%with_mi..] extension point, the corresponding access operator
[%with_mi s.[a;b] ] will be rewritten to s.[Cons(a,Cons(b,Nil))]. The same feature is available for string, array abd bigarray indices through
respectively the String_indices, Array_indices and Bigarray_indices constructors.