1 import Base 2 import ./deferred_command.bend as Deferred 3 import ./trace_facts.bend as TraceFacts 4 import 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. 9 type Pending is Data: 10 NoPending{} 11 Queued{value: V, skipSame: Bool} 12 13 type Transition is Data: 14 Transition{from: V, to: V} 15 Initial{to: V} 16 17 type Slot is Data: 18 Missing{} 19 Present{current: V, pending: Pending, previous: Maybe<&2,V>, changed: Bool} 20 21 # Public capability projections intentionally expose less than debug metadata. 22 type CurrentView is Data: 23 Unavailable{} 24 Current{value: V} 25 type NextView is Data: 26 NoNext{} 27 Next{value: V} 28 29 def current_view(~S: Data,~M: Data,~V: Data,slot: Slot) -> CurrentView: 30 match slot: 31 case Missing{}: Unavailable{} 32 case Present{current,_,_,_}: Current{current} 33 def pending_view(~S: Data,~M: Data,~V: Data,pending: Pending) -> NextView: 34 match pending: 35 case NoPending{}: NoNext{} 36 case Queued{value,_}: Next{value} 37 def next_view(~S: Data,~M: Data,~V: Data,slot: Slot) -> NextView: 38 match slot: 39 case Missing{}: NoNext{} 40 case Present{_,pending,_,_}: pending_view(~S,~M,~V,pending) 41 42 def is_current(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,view: CurrentView,value: V) -> Bool: 43 match view: 44 case Unavailable{}: False{} 45 case Current{current}: same(current,value) 46 47 type Family W & Slot,-put: W -> Slot -> W> is Data: 49 Family{name: String} 50 51 type QueueInput is Data: 52 Set{value: V,skipSame: Bool} 53 Reset{} 54 55 type Snapshot is Data: 56 Unscheduled{} 57 Scheduled{value: V, skipSame: Bool} 58 59 type Applied is Data: 60 Applied{slot: Slot, event: Maybe<&2,Transition>} 61 62 def queue(~S: Data,~M: Data,~V: Data,slot: Slot,value: V,skipSame: Bool) -> Slot: 63 match slot: 64 case Missing{}: Missing{} 65 case Present{current,_,previous,changed}: Present{current,Queued{value,skipSame},previous,changed} 66 67 def reset(~S: Data,~M: Data,~V: Data,slot: Slot) -> Slot: 68 match slot: 69 case Missing{}: Missing{} 70 case Present{current,_,previous,changed}: Present{current,NoPending{},previous,changed} 71 72 def clear_changed(~S: Data,~M: Data,~V: Data,slot: Slot) -> Slot: 73 match slot: 74 case Missing{}: Missing{} 75 case Present{current,pending,previous,_}: Present{current,pending,previous,False{}} 76 77 def snapshot_pending(~V: Data,pending: Pending) -> Snapshot: 78 match pending: 79 case NoPending{}: Unscheduled{} 80 case Queued{value,skipSame}: Scheduled{value,skipSame} 81 82 def snapshot(~S: Data,~M: Data,~V: Data,slot: Slot) -> Snapshot: 83 match slot: 84 case Missing{}: Unscheduled{} 85 case Present{_,pending,_,_}: snapshot_pending(~V,pending) 86 87 def apply_decision(~S: Data,~M: Data,~V: Data,+current: V,+value: V,previous: Maybe<&2,V>,changed: Bool,skip: Bool) -> Applied: 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 92 def 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: 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. 97 def apply_snapshot(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,slot: Slot,captured: Snapshot) -> Applied: 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. 106 def write_taken(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 107 ~put: W -> Slot -> W, 108 pair: W & Slot,undo: List<&1,(W -> W)>,commands:List<&1,Deferred.Command>,events: List<&2,E>,trace:TraceFacts.State,value: V,skipSame: Bool) -> Tx.Tx: 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 113 def write(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 114 ~take: W -> W & Slot,~put: W -> Slot -> W, 115 tx: Tx.Tx,value: V,skipSame: Bool) -> Tx.Tx: 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 120 def reset_taken(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 121 ~put: W -> Slot -> W, 122 pair: W & Slot,undo: List<&1,(W -> W)>,commands:List<&1,Deferred.Command>,events: List<&2,E>,trace:TraceFacts.State) -> Tx.Tx: 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 127 def reset_tx(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 128 ~take: W -> W & Slot,~put: W -> Slot -> W, 129 tx: Tx.Tx) -> Tx.Tx: 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 134 def next(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 135 ~take: W -> W & Slot,~put: W -> Slot -> W, 136 tx: Tx.Tx,request: QueueInput) -> Tx.Tx: 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 141 def read_taken(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 142 pair: W & Slot,undo: List<&1,(W -> W)>,commands:List<&1,Deferred.Command>,events: List<&2,E>,trace:TraceFacts.State) -> Tx.Tx & Slot: 143 match pair: 144 case (world,slot): (Tx.Tx{world,undo,commands,events,trace},slot) 145 146 def read(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 147 ~take: W -> W & Slot,tx: Tx.Tx) -> Tx.Tx & Slot: 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 151 def read_declared(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 152 ~take: W -> W & Slot,~put: W -> Slot -> W, 153 _: Family,tx: Tx.Tx) -> Tx.Tx & Slot: 154 read(~S,~M,~V,~W,~E,~take,tx) 155 156 def next_declared(~S: Data,~M: Data,~V: Data,~W: Type,~E: Data, 157 ~take: W -> W & Slot,~put: W -> Slot -> W, 158 _: Family,tx: Tx.Tx,request: QueueInput) -> Tx.Tx: 159 next(~S,~M,~V,~W,~E,~take,~put,tx,request) 160 161 type Condition is Data: 162 InState{value: V} 163 StateChanged{} 164 Not{inner: Condition} 165 Constant{value: Bool} 166 And{left: Condition,right: Condition} 167 Or{left: Condition,right: Condition} 168 169 def current_matches(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,slot: Slot,value: V) -> Bool: 170 match slot: 171 case Missing{}: False{} 172 case Present{current,_,_,_}: same(current,value) 173 174 def changed(~S: Data,~M: Data,~V: Data,slot: Slot) -> 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. 181 def all(~V: Data,items: List<&2,Condition>) -> Condition: 182 match items: 183 case Nil{}: Constant{True{}} 184 case Con{head,tail}: And{head,all(~V,tail)} 185 186 def any(~V: Data,items: List<&2,Condition>) -> Condition: 187 match items: 188 case Nil{}: Constant{False{}} 189 case Con{head,tail}: Or{head,any(~V,tail)} 190 191 def evaluate(~S: Data,~M: Data,~V: Data,~same: V -> V -> Bool,condition: Condition,+slot: Slot) -> 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. 202 type Marker is Type: 203 Marker{steps: List<&1,(W -> W)>} 204 205 def 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 210 def run_marker(~S: Data,~W: Type,marker: Marker,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. 216 type OwnedMarker is Type: 217 OwnedMarker{steps: List<&1,(H -> W -> H & W)>} 218 219 def 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 223 def 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 228 def run_owned_marker(~S: Data,~W: Type,~H: Type,marker: OwnedMarker,owners: H,world: W) -> H & W: 229 match marker: 230 case OwnedMarker{steps}: owned_marker_steps(~W,~H,steps,owners,world)