- OCaml 81.7%
- Shell 15.8%
- Makefile 2.5%
| .gitignore | ||
| case.ml | ||
| error.sh | ||
| fieldglass.install | ||
| fieldglass.ml | ||
| fieldglass.mli | ||
| fieldglass.opam | ||
| Makefile | ||
| META | ||
| README.md | ||
| record.ml | ||
| rewrite.ml | ||
| runtime.ml | ||
| smoke.sh | ||
fieldglass
Generates accessors and composable lenses from OCaml record declarations, allowing a caller to read or replace a nested field through ordinary functions while retaining the surrounding record structure.
type swatch = { colour : string; label : string }
[@@fieldglass generate]
let grey = { colour = "grey"; label = "sample" }
let blue = set_colour "blue" grey
let labelled = update_label ~f:String.uppercase_ascii grey
let colour = Fieldglass.view _colour blue
For each field, the rewriter emits a getter, a setter prefixed with set_, an
updater prefixed with update_, and a getter/setter pair whose name begins
with _. A setter takes the replacement value followed by the source record,
while an updater applies its labelled ~f argument to the existing field
value and constructs a replacement record containing the result.
Generated updates retain the unfocussed fields and perform no assignment to
the source record, including when the declaration contains mutable fields.
Both [@@fieldglass generate] and [@@lens generate] select this expansion,
which produces ordinary OCaml functions that can be used independently of
the Fieldglass runtime's composition operations.
Build
Run make to compile the runtime archive and PPX executable, or make test
to also check lens operations, generated record accessors, configuration
errors, and linkage against an explicit module interface.
To compile a consumer, pass the rewriter through -ppx and link the runtime
archive before the source file, as in the following invocation:
ocamlc -ppx ./rewrite fieldglass.cma example.ml -o example
The command OPAMBUILDTEST=true opam pin add fieldglass . builds and tests
the package before installing the fieldglass rewriter and runtime library.
Use the same OCaml compiler for the rewriter and its consumers, since the
PPX exchanges compiler syntax trees and links against the compiler libraries.
Lenses
Fieldglass.lens ~view ~set constructs a pair with representation
('s -> 'a) * ('b -> 's -> 't), where the getter extracts a value from the
source and the setter combines a replacement with that source to produce
the result. The separate type parameters permit a lens to change the type
of its focussed value when the enclosing structure admits that change.
view applies the getter, set applies the setter, and over ~f passes the
getter's result through f before supplying it to the setter alongside the
original source. With compose outer inner, reading follows the outer getter
and then the inner getter; replacement updates the inner structure before
passing it back through the outer setter, preserving the surrounding data
according to the supplied setters.
Fieldglass.Infix exposes these operations as ^., ^~, and ^%, with
^> composing an outer lens with an inner lens and ^< accepting the same
operands in reverse order. The runtime also supplies tuple projections and
the identity lens _id, together with _hd and _tl for focussing on a
list's head or tail; either list lens raises Invalid_argument when asked
to read or update an empty list.
Configuration
Supply labelled options after generate to control the generated names,
argument order, and selection of operations, using identifiers or strings
for prefixes and argument names. Boolean options accept a bare label as an
enabled flag, or an explicit true or false value when the configuration
needs to specify the setting directly.
| Option | Behaviour |
|---|---|
~field_prefix:name |
Add a prefix to each field name. |
~field_prefix_from_type |
Use the record type name as the prefix. |
~get_prefix:name |
Prefix getter names. |
~set_prefix:name |
Replace the set prefix. |
~update_prefix:name |
Replace the update prefix. |
~self_arg_first |
Put the record first in setters and updaters. |
~func_named_arg:name |
Rename the updater's f argument. |
~func_no_named_arg |
Make the updater's function argument positional. |
~no_get |
Omit getters. |
~no_set |
Omit setters. |
~no_update |
Omit updaters. |
~no_lens |
Omit lens pairs. |
~just_lens |
Generate lens pairs without named accessors. |
Before applying naming options, the rewriter removes the longest shared
field prefix ending at an underscore boundary, so fields such as
paint_colour and paint_label produce the base names colour and label.
A record containing only one field keeps that field's complete name, while
generated lens pairs retain replacement-first setters even when
~self_arg_first changes the argument order of the named accessors.
An annotation attached to a mutually recursive record declaration applies
to the entire declaration group, whose options the rewriter processes from
left to right with later settings taking precedence. When records share
field labels, ~field_prefix_from_type gives their accessors distinct names;
the rewriter rejects duplicate generated bindings and repeated options
within a single annotation with a compilation error.
Place generation annotations on record declarations in implementations and
declare the exported accessor types explicitly in module signatures, since
the rewriter emits value bindings and rejects generation annotations in
signatures. For private records or records containing universally quantified
fields, select ~no_set ~no_update ~no_lens to generate getters without
requesting replacement operations that the rewriter does not generate for
those declarations.