The stack of bind left-hand sides currently in effect is the current "scope". In order to create a function in one scope and apply it in a different scope, one must manually save and restore the scope. Essentially, the scope should be part of every closure that constructs incrementals. For example:
bind t1 ~f:(fun i1 -> let f t2 = map t2 ~f:(fun i2 -> i1 + i2) in bind t3 ~f:(fun i -> ... f ...); bind t4 ~f:(fun i -> ... f ...));
In the above code, the calls to
f will create a map node that unnecessarily depends on the left-hand side of the most recent bind (
t4). To eliminate the unnecessary dependence, one should save and restore the scope for
bind t1 ~f:(fun i1 -> let scope = Scope.current () in let f t2 = Scope.within scope ~f:(fun () -> map t2 ~f:(fun i2 -> i1 + i2)) in bind t3 ~f:(fun i -> ... f ...); bind t4 ~f:(fun i -> ... f ...))
memoize_fun capture some common situations in which one would otherwise need to use
val top : t
top is the toplevel scope.
val current : unit -> t
current () returns the scope in which a node would be created right now.
val within : t -> f:( unit -> 'a ) -> 'a
within t f runs
f in scope
t, which causes all nodes created by
f to be in scope
t. An exception raised by
f will be raised by
within in the usual way.