machine.bend

bendvy-ecs-core@0.1.0.3 · 185 lines · 39 definitions

1import Base
2
3# Experimental machine metadata. World and component owners remain arbitrary Type.
4# This module follows the pinned reference marker snapshot semantics; that behavior
5# is not approval of the disputed blanket next-marker retention requirement.
6type Pending<V: Data> is Data:
7  NoPending{}
8  Queued{value: V, skipSame: Bool}
9
10type Transition<V: Data> is Data:
11  Transition{from: V, to: V}
12  Initial{to: V}
13
14type Slot<S: Data, M: Data, V: Data> is Data:
15  Missing{}
16  Present{current: V, pending: Pending<V>, previous: Maybe<&2,V>, changed: Bool}
17
18# Public capability projections intentionally expose less than debug metadata.
19type CurrentView<S: Data,M: Data,V: Data> is Data:
20  Unavailable{}
21  Current{value: V}
22type NextView<S: Data,M: Data,V: Data> is Data:
23  NoNext{}
24  Next{value: V}
25
26def current_view_generic(-S: Data,-M: Data,-V: Data,slot: Slot<S,M,V>) -> CurrentView<S,M,V>:
27  match slot:
28    case Missing{}: Unavailable{}
29    case Present{current,_,_,_}: Current{current}
30def current_view(~S: Data,~M: Data,~V: Data,slot: Slot<S,M,V>) -> CurrentView<S,M,V>:
31  current_view_generic(S,M,V,slot)
32
33def pending_view_generic(-S: Data,-M: Data,-V: Data,pending: Pending<V>) -> NextView<S,M,V>:
34  match pending:
35    case NoPending{}: NoNext{}
36    case Queued{value,_}: Next{value}
37def pending_view(~S: Data,~M: Data,~V: Data,pending: Pending<V>) -> NextView<S,M,V>:
38  pending_view_generic(S,M,V,pending)
39
40def next_view_generic(-S: Data,-M: Data,-V: Data,slot: Slot<S,M,V>) -> NextView<S,M,V>:
41  match slot:
42    case Missing{}: NoNext{}
43    case Present{_,pending,_,_}: pending_view_generic(S,M,V,pending)
44
45def next_view(~S: Data,~M: Data,~V: Data,slot: Slot<S,M,V>) -> NextView<S,M,V>:
46  next_view_generic(S,M,V,slot)
47
48def is_current(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,view: CurrentView<S,M,V>,value: V) -> Bool:
49  match view:
50    case Unavailable{}: False{}
51    case Current{current}: same(current,value)
52
53type Family<S: Data,M: Data,V: Data,W: Type,
54  -take: W -> W & Slot<S,M,V>,-put: W -> Slot<S,M,V> -> W> is Data:
55  Family{name: String}
56
57type QueueInput<V: Data> is Data:
58  Set{value: V,skipSame: Bool}
59  Reset{}
60
61type Snapshot<V: Data> is Data:
62  Unscheduled{}
63  Scheduled{value: V, skipSame: Bool}
64
65type Applied<S: Data, M: Data, V: Data> is Data:
66  Applied{slot: Slot<S,M,V>, event: Maybe<&2,Transition<V>>}
67
68def queue_generic(-S: Data,-M: Data,-V: Data,slot: Slot<S,M,V>,value: V,skipSame: Bool) -> Slot<S,M,V>:
69  match slot:
70    case Missing{}: Missing{}
71    case Present{current,_,previous,changed}: Present{current,Queued{value,skipSame},previous,changed}
72
73def queue(~S: Data,~M: Data,~V: Data,slot: Slot<S,M,V>,value: V,skipSame: Bool) -> Slot<S,M,V>:
74  queue_generic(S,M,V,slot,value,skipSame)
75
76def reset_generic(-S: Data,-M: Data,-V: Data,slot: Slot<S,M,V>) -> Slot<S,M,V>:
77  match slot:
78    case Missing{}: Missing{}
79    case Present{current,_,previous,changed}: Present{current,NoPending{},previous,changed}
80
81def reset(~S: Data,~M: Data,~V: Data,slot: Slot<S,M,V>) -> Slot<S,M,V>:
82  reset_generic(S,M,V,slot)
83
84def clear_changed(~S: Data,~M: Data,~V: Data,slot: Slot<S,M,V>) -> Slot<S,M,V>:
85  match slot:
86    case Missing{}: Missing{}
87    case Present{current,pending,previous,_}: Present{current,pending,previous,False{}}
88
89def snapshot_pending(~V: Data,pending: Pending<V>) -> Snapshot<V>:
90  match pending:
91    case NoPending{}: Unscheduled{}
92    case Queued{value,skipSame}: Scheduled{value,skipSame}
93
94def snapshot(~S: Data,~M: Data,~V: Data,slot: Slot<S,M,V>) -> Snapshot<V>:
95  match slot:
96    case Missing{}: Unscheduled{}
97    case Present{_,pending,_,_}: snapshot_pending(~V,pending)
98
99def apply_decision(~S: Data,~M: Data,~V: Data,+current: V,+value: V,previous: Maybe<&2,V>,changed: Bool,skip: Bool) -> Applied<S,M,V>:
100  match skip:
101    case True{}: Applied{Present{current,NoPending{},previous,changed},None{}}
102    case False{}: Applied{Present{value,NoPending{},Some{current},True{}},Some{Transition{current,value}}}
103
104def apply_present(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,+current: V,+value: V,previous: Maybe<&2,V>,changed: Bool,skipSame: Bool) -> Applied<S,M,V>:
105  apply_decision(~S,~M,~V,current,value,previous,changed,Bool.and(skipSame,same(current,value)))
106
107# Captured initial value is authoritative; current pending is deleted at the
108# machine's turn, matching Runtime.ts:1534 even when an earlier hook overwrote it.
109def apply_snapshot(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,slot: Slot<S,M,V>,captured: Snapshot<V>) -> Applied<S,M,V>:
110  match slot captured:
111    case slot Unscheduled{}: Applied{slot,None{}}
112    case Missing{} Scheduled{_,_}: Applied{Missing{},None{}}
113    case Present{current,_,previous,changed} Scheduled{value,skipSame}:
114      apply_present(~S,~M,~V,~same,current,value,previous,changed,skipSame)
115
116type Condition<V: Data> is Data:
117  InState{value: V}
118  StateChanged{}
119  Not{inner: Condition<V>}
120  Constant{value: Bool}
121  And{left: Condition<V>,right: Condition<V>}
122  Or{left: Condition<V>,right: Condition<V>}
123
124def current_matches(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,slot: Slot<S,M,V>,value: V) -> Bool:
125  match slot:
126    case Missing{}: False{}
127    case Present{current,_,_,_}: same(current,value)
128
129def changed(~S: Data,~M: Data,~V: Data,slot: Slot<S,M,V>) -> Bool:
130  match slot:
131    case Missing{}: False{}
132    case Present{_,_,_,flag}: flag
133
134# Arbitrary public variadic combinations become a structural Boolean tree.
135# Empty and/or preserve the primary API identities.
136def all(~V: Data,items: List<&2,Condition<V>>) -> Condition<V>:
137  match items:
138    case Nil{}: Constant{True{}}
139    case Con{head,tail}: And{head,all(~V,tail)}
140
141def any(~V: Data,items: List<&2,Condition<V>>) -> Condition<V>:
142  match items:
143    case Nil{}: Constant{False{}}
144    case Con{head,tail}: Or{head,any(~V,tail)}
145
146def evaluate(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,condition: Condition<V>,+slot: Slot<S,M,V>) -> Bool:
147  match condition:
148    case InState{value}: current_matches(~S,~M,~V,~same,slot,value)
149    case StateChanged{}: changed(~S,~M,~V,slot)
150    case Not{inner}: Bool.not(evaluate(~S,~M,~V,~same,inner,slot))
151    case Constant{value}: value
152    case And{left,right}: Bool.and(evaluate(~S,~M,~V,~same,left,slot),evaluate(~S,~M,~V,~same,right,slot))
153    case Or{left,right}: Bool.or(evaluate(~S,~M,~V,~same,left,slot),evaluate(~S,~M,~V,~same,right,slot))
154
155# A heterogeneous marker snapshot owns each callback exactly once. Each callback
156# captures a Data machine value and closed lenses, never a world/component owner.
157type Marker<S: Data,W: Type> is Type:
158  Marker{steps: List<&1,(W -> W)>}
159
160def marker_steps(~W: Type,steps: List<&1,(W -> W)>,world: W) -> W:
161  match steps:
162    case Nil{}: world
163    case Con{step,rest}: marker_steps(~W,rest,step(world))
164
165def run_marker(~S: Data,~W: Type,marker: Marker<S,W>,world: W) -> W:
166  match marker:
167    case Marker{steps}: marker_steps(~W,steps,world)
168
169# Registered schedule/transition owners live alongside the world and are also
170# threaded, rather than copied into one closure per machine.
171type OwnedMarker<S: Data,W: Type,H: Type> is Type:
172  OwnedMarker{steps: List<&1,(H -> W -> H & W)>}
173
174def marker_next(~W: Type,~H: Type,next: H -> W -> H & W,pair: H & W) -> H & W:
175  (owners,world)=pair
176  next(owners,world)
177
178def owned_marker_steps(~W: Type,~H: Type,steps: List<&1,(H -> W -> H & W)>,owners: H,world: W) -> H & W:
179  match steps:
180    case Nil{}: (owners,world)
181    case Con{step,rest}: marker_next(~W,~H,h => w => owned_marker_steps(~W,~H,rest,h,w),step(owners,world))
182
183def run_owned_marker(~S: Data,~W: Type,~H: Type,marker: OwnedMarker<S,W,H>,owners: H,world: W) -> H & W:
184  match marker:
185    case OwnedMarker{steps}: owned_marker_steps(~W,~H,steps,owners,world)