ppx_update
is a small utility ppx for optimizing record updates with certain patterns.
A pattern one comes across when writing certain kinds of OCaml programs is:
type a = {...}
type b = {...}
type c = {a:a; b:b; ...}
let update c =
let a' = ... in
let b' = ... in
{c with a'; b'; ... }
Now, if a'
and b'
are the same as c
's previous values a
and b
, what we want to do for efficiency is this:
let update c =
let a' = ... in
let b' = ... in
if c.a == a' && c.b == b' then
c
else
{c with a'; b'; ... }
Physical equality is a very quick check that helps us avoid allocation when it's not needed.
ppx_update
lets you do this more easily:
let update c =
let a' = ... in
let b' = ... in
[%up {c with a'; b'; ... }] (* will automatically take care of comparisons *)
Similarly, if b
is mutable:
type a = {...}
type c = {mutable a:a; ...}
let update c =
let b' = ... in
c.b <- b'
In this case, we want to try and avoid the write barrier in case b' == b
, which we can check for with
let update c =
let b' = ... in
if c.b == b' then
()
else
c.b <- b'
With ppx_update
:
let update c =
let b' = ... in
[%upf c.b <- b'] (* will only update if there's no physical address match *)
Simply add to your preprocessing sections in dune
; In your library or executable section
(libraries ppx_update)
(preprocess (ppx ppx_update))
Two extensions are currently supported:
%up
(forupdate
): handles record update. Will only work if you have a record that is functionally updated using thewith
keyword.%upf
(forupdate_field
): handles mutable field updates.
Note that ppx_update
is simple, so if you update with a long expression, that expression will be computed twice.
For example, this is good:
c.foo <- foo';
but this is not a good idea:
c.foo <- long_computation a b c;
since the long computation will be performed twice.