This PR replaces the FRP machinery with a new implementation, inspired by Jane Street's Incremental.
The main thing it fixes is sharing. In the current implementation, if we have
d :: Dynamic A
f :: A -> B
d2 = map f d
then when d changes, f will be recomputed once for each subscriber of d2. With the new backend, it will be computed once per change (as it should).
This is really significant when we consider more complex dependency network, and many subscribers (think 100s of tiny GUI widgets). In production we've seen cases where a function was unnecessarily computed 600 times.
Additionally the new backend is written with performance in mind. In particular:
EffectFnN is used where possible, to avoid allocating closures.
Mutability everywhere.
The Node type is a mutable record instead of an immutable record of mutable Refs, to avoid indirection.
Instead of Maybe, an unboxed Optional type is used, which represents None with a sentinel value, and everything else as is. Again, to avoid indirection and memory allocation.
if is used instead of when in Effect context, to generate better code with MagicDo.
This PR replaces the FRP machinery with a new implementation, inspired by Jane Street's Incremental.
The main thing it fixes is sharing. In the current implementation, if we have
then when
d
changes,f
will be recomputed once for each subscriber of d2. With the new backend, it will be computed once per change (as it should).This is really significant when we consider more complex dependency network, and many subscribers (think 100s of tiny GUI widgets). In production we've seen cases where a function was unnecessarily computed 600 times.
Additionally the new backend is written with performance in mind. In particular:
EffectFnN
is used where possible, to avoid allocating closures.Node
type is a mutable record instead of an immutable record of mutableRef
s, to avoid indirection.Maybe
, an unboxedOptional
type is used, which representsNone
with a sentinel value, and everything else as is. Again, to avoid indirection and memory allocation.if
is used instead ofwhen
inEffect
context, to generate better code withMagicDo
.TODO: