machine.bend

bendvy-ecs-core@0.1.0.0 · 230 lines · 0 definitions

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