OCaml preprocessor extension for generating lens functions.

Milo Turner 9e26ed26c0 Add installation section to README 6 jaren geleden
lib 80849e1127 Created 'lenslib' package 6 jaren geleden
src c1c88c7cd1 Automatically remove prefix occuring in all record fields 6 jaren geleden
test c1c88c7cd1 Automatically remove prefix occuring in all record fields 6 jaren geleden
.gitignore 5bcc55c0b1 Initial dune project 6 jaren geleden
Makefile b17abd5cec Fix up ppx_lens.opam file for deployment 6 jaren geleden
README.md 9e26ed26c0 Add installation section to README 6 jaren geleden
lenslib.opam 71ffed1275 Fix up lenslib.opam for deployment. 6 jaren geleden
ppx_lens.opam b17abd5cec Fix up ppx_lens.opam file for deployment 6 jaren geleden
ppx_lens_tests.opam b17abd5cec Fix up ppx_lens.opam file for deployment 6 jaren geleden

README.md

ppx_lens

ppx_lens is an OCaml PPX for generating "lens" operations on records, which helps to facilitate functional updates on deeply nested data structures.

Examples

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 field
  • set_FIELDNAME: function for changing the value of a field
  • update_FIELDNAME: function for changing the value of a field with a function

Additionally 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.

Installation

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 ...)))))

Configuration

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

Lenslib

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

Examples

# 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