src/lazy/lazy.bend
bolt@1.23.0.0 · 49 lines · 0 definitions
1# lazy: branches that do not run both ways. Bool.pick, Bool.and and Bool.or
2# are ordinary defs, so every argument is evaluated before the call: a
3# search written as `Bool.pick(T, hit, here, go(rest))` walks the whole list
4# whatever `hit` says (measured native, 100 iterations over 100k cells: the
5# hit at the head costs 0.10 s, the same as the hit at the end, while the
6# forms here cost 0.00 s). A thunk `Unit -> T` is a value, so the branch
7# that is not taken is never applied. Keep them for work that does not
8# recurse: a thunk around a search's own recursive step allocates a closure
9# and leaves the loop on every step, where carrying the test as a Bool into
10# the next call keeps it a loop (the `thunk` rule, U015).
11#
12# No law can say that a branch is not taken, since a law states what a term
13# equals, not what evaluation skipped: each form here matches on the Bool
14# before it applies the thunk, and laws/LazyMatchesEagerCombinators.bend proves the
15# values agree with the strict forms.
16import Base
17
18# `a` when c, else what rest gives: the early exit of a search
19def stop(-A: Type, cc: Bool, aa: A, rest: Unit -> A) -> A:
20 match cc:
21 case True{}:
22 aa
23 case False{}:
24 rest(Unit{})
25
26# True when a, else what rest gives: `or` that stops at the first True
27def or_else(aa: Bool, rest: Unit -> Bool) -> Bool:
28 match aa:
29 case True{}:
30 True{}
31 case False{}:
32 rest(Unit{})
33
34# what rest gives when a, else False: `and` that stops at the first False
35def and_then(aa: Bool, rest: Unit -> Bool) -> Bool:
36 match aa:
37 case True{}:
38 rest(Unit{})
39 case False{}:
40 False{}
41
42# what yes gives when c, else what no gives: a pick whose two branches each
43# recurse, only the one taken run
44def either(-A: Type, cc: Bool, yes: Unit -> A, no: Unit -> A) -> A:
45 match cc:
46 case True{}:
47 yes(Unit{})
48 case False{}:
49 no(Unit{})