Skip to content

Instantly share code, notes, and snippets.

@khooyp
Last active January 27, 2020 03:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save khooyp/98abc0e64dc296deaa48 to your computer and use it in GitHub Desktop.
Save khooyp/98abc0e64dc296deaa48 to your computer and use it in GitHub Desktop.
Example of Incremental's height-based evaluation order leading to change propagation inconsistent with from-scratch evaluation, compared to Classic Adapton
module Inc = Incremental_lib.Incremental.Make ()
open Inc
let make init =
let num = 4 in
let den_v = Var.create init in
let den = Var.watch den_v in
(* computes "if den = 0 then 0 else num / den" *)
let div = bind den (fun den -> return (num / den)) in
let den0 = bind den (fun den -> return (den = 0)) in
let res = bind den0 (fun den0 -> if den0 then (return 0) else div) in (* should not lead to Division_by_zero *)
(* alternatively, let res = if_ den0 (return 0) div in *)
(den_v, observe res)
let () =
(* from-scratch computation with initial input = 0 *)
let den_v, res_o = make 0 in
stabilize ();
Printf.printf "%d\n%!" (Observer.value_exn res_o); (* prints 0 *)
(* from-scratch computation with initial input = 2 *)
let den_v, res_o = make 2 in
stabilize ();
Printf.printf "%d\n%!" (Observer.value_exn res_o); (* prints 2 *)
(* incremental computation with changed input = 0 *)
Var.set den_v 0;
stabilize (); (* Exception: Division_by_zero *)
Printf.printf "%d\n%!" (Observer.value_exn res_o)
module ABool = Adapton.Make (AdaptonUtil.Types.Bool)
module AInt = Adapton.Make (AdaptonUtil.Types.Int)
let make init =
let num = 4 in
let den = AInt.const init in
(* computes "if den = 0 then 0 else num / den" *)
let div = AInt.thunk (fun () -> num / AInt.force den) in
let den0 = ABool.thunk (fun () -> AInt.force den = 0) in
let res = AInt.thunk (fun () -> if ABool.force den0 then 0 else AInt.force div) in (* should not lead to Division_by_zero *)
(den, res)
let () =
(* from-scratch computation with initial input = 0 *)
let den, res = make 0 in
Printf.printf "%d\n%!" (AInt.force res); (* prints 0 *)
(* from-scratch computation with initial input = 2 *)
let den, res = make 2 in
Printf.printf "%d\n%!" (AInt.force res); (* prints 2 *)
(* incremental computation with changed input = 0 *)
AInt.update_const den 0;
Printf.printf "%d\n%!" (AInt.force res) (* prints 0 *)
@matthewhammer
Copy link

For reference, here's the same test in the latest version of Adapton in Rust:
https://github.com/cuplv/adapton.rust/blob/08f02947e8ebfaf57f7623bf71cb51b368fefa5c/tests/engine.rs#L13

And here's another variation of the same pattern: A condition switches after run #1, and by doing change propagation in a demand-driven way, we avoid doing a needless, expensive sub-computation:
https://github.com/cuplv/adapton.rust/blob/08f02947e8ebfaf57f7623bf71cb51b368fefa5c/tests/engine.rs#L42

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment