Last active
January 27, 2020 03:57
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 *) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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