ppx_lens
is an OCaml PPX for generating "lens" operations on records, which
helps to facilitate functional updates on deeply nested data structures.
Generate lens functions for a record:
# type glasses =
{ len : int;
bridge : int;
diam : int }
[@@lens generate] ;;
type glasses = { len : int; bridge : int; diam : int; }
val len : glasses -> int = <fun>
val bridge : glasses -> int = <fun>
val diam : glasses -> int = <fun>
val set_len : int -> glasses -> glasses = <fun>
val set_bridge : int -> glasses -> glasses = <fun>
val set_diam : int -> glasses -> glasses = <fun>
val update_len : f:(int -> int) -> glasses -> glasses = <fun>
val update_bridge : f:(int -> int) -> glasses -> glasses = <fun>
val update_diam : f:(int -> int) -> glasses -> glasses = <fun>
val _len : (glasses -> int) * (int -> glasses -> glasses) = (<fun>, <fun>)
val _bridge : (glasses -> int) * (int -> glasses -> glasses) = (<fun>, <fun>)
val _diam : (glasses -> int) * (int -> glasses -> glasses) = (<fun>, <fun>)
# let g = set_diam 56 {len = 145; bridge = 17; diam = 54} ;;
val g : glasses = {len = 145; bridge = 17; diam = 56}
[@@lens generate]
generates a few functions for each field of the record, specifically:
FIELDNAME
: function for getting the value of a fieldset_FIELDNAME
: function for changing the value of a fieldupdate_FIELDNAME
: function for changing the value of a field with a functionAdditionally it generates "lens" values (denoted with underscore prefix), which are just
pairs consisting of the getter and setter functions. See lenslib
below for ways to
manipulate these values.
ppx_lens
is best installed with OPAM and dune.
$ git clone <url>/ppx_lens.git
$ opam pin add ppx_lens ppx_lens/
$ opam pin add lenslib ppx_lens/ # optional
TODO: publish to OPAM servers
dune (jbuilder) configuration:
(library
((name ...)
....
(preprocess
(pps (... ppx_lens ...)))))
The functions generated by ppx_lens
can be configured by supplying named arguments.
Usage:
type t = { <record field> ... }
[@@lens generate <parameter> ...]
Availabe parameters:
~field_prefix:<prefix>
: change what prefix to place before the field names~field_prefix_from_type
: use the name of the type to determine the prefix~self_arg_first
: place the argument for the structure being modified
before the other arguments, in setter / updater functions. By default, the
structure comes after the other argument.~get_prefix:<prefix>
: change what prefix to use for getter functions. By
default, getter functions do not have a prefix.~set_prefix:<prefix>
: change what prefix to use for setter functions. By
default, setter functions use the prefix set
.~update_prefix:<prefix>
: change what prefix to use for updater functions. By
default, updater functions use the prefix update
.~func_named_arg:<name>
: change the named argument in the updater function. By
default, the argument is named f
.~func_no_named_arg
: don't name the argument in the updater function. The position
of the argument may then be controlled with ~self_arg_first
.~no_get
: don't generate getter functions.~no_set
: don't generate setter functions.~no_update
: don't generate updater functions.~no_lens
: don't generate lens values.~just_lens
: don't generate getter / setter / updater functions, just lens values.TODO: example here
This repository contains a small library called lenslib
with functions for manipulating
the "lens" values generated by ppx_lens
. Note that lenslib
is not a dependency for
ppx_lens
.
module Lenslib :
sig
type ('s, 't, 'a, 'b) lens = ('s -> 'a) * ('b -> 's -> 't)
type ('s, 'a) lens' = ('s, 's, 'a, 'a) lens
val _1 : ('x * 'a, 'y * 'a, 'x, 'y) lens
val _2 : ('a * 'x, 'a * 'y, 'x, 'y) lens
module Lens :
sig
type ('s, 't, 'a, 'b) t = ('s, 't, 'a, 'b) lens
type ('s, 'a) t' = ('s, 'a) lens'
val view : ('s, 't, 'a, 'b) t -> 's -> 'a
val set : ('s, 't, 'a, 'b) t -> 's -> 'b -> 't
val over : ('s, 't, 'a, 'b) t -> f:('a -> 'b) -> 's -> 't
val compose : ('a, 'b, 'c, 'd) t -> ('c, 'd, 'e, 'f) t -> ('a, 'b, 'e, 'f) t
val ( ^. ) : 'a -> ('a, 'b, 'c, 'd) t -> 'c
val ( ^> ) : ('a, 'b, 'c, 'd) t -> ('c, 'd, 'e, 'f) t -> ('a, 'b, 'e, 'f) t
end
end
# open Lenslib
# open Lens
# g ^. _bridge ;;
- : int = 17
# set (_2 ^> _len) (true, g) 160 ;;
- : bool * glasses = (true, {len = 160; bridge = 17; diam = 56})
TODO: create ocamldoc