1 import Base 2 import ./change_tick.bend as Tick 3 import ./clock_state.bend as Clock 4 import ./deferred_command.bend as Deferred 5 import ./entity_key_render.bend as KeyRender 6 import ./trace_facts.bend as TraceFacts 7 import lifecycle.bend as L 8 import column.bend as Col 9 import world.bend as W 10 import transaction.bend as T 11 import entity_key.bend as K 12 import entity_allocator.bend as A 13 14 # Caller-authored schema lenses are trusted provisioning. The gameplay callback 15 # receives only universally quantified provider handles, never these owners. 16 # Trusted schema provisioning for one component column and detached Data view V. 17 # take/put must return the actual affine storage owners; project returns C with V. 18 # The nominal Token distinguishes families; the display name is not access proof. 19 type Family<-S: Data,-Store: Type,-Rest: Type,-C: Type,-V: Data,-Token: Data,-take: Store -> Rest & Col.Column,-put: Rest -> Col.Column -> Store,-project: C -> C & V> is Data: 20 Family{name: String} 21 # Detached read result: Found holds V, ComponentAbsent means a valid entity lacks 22 # this component, and MissingEntity covers invalid, stale or foreign handles. 23 type Access is Data: 24 Found{value: V} 25 ComponentAbsent{} 26 MissingEntity{} 27 # Immediate replacement result retaining World in every branch. Replaced returns 28 # the displaced actual payload owner; rejection returns the incoming optional owner 29 # and error. Neither Type payload is copied into detached observation data. 30 type Replacement is Type: 31 Replaced{world: W,previous: Maybe<&1,C>} 32 ReplaceRejected{world: W,incoming: Maybe<&1,C>,error: W.WorldError} 33 # Internal storage-adapter result carrying the displaced or rejected owner. 34 # It is not a handle-validation result or a gameplay capability. 35 type Swap is Type: 36 Swapped{previous: Maybe<&1,C>} 37 SwapRejected{incoming: Maybe<&1,C>} 38 # Transactional write result. Written retains the Tx with its inverse journal; 39 # WriteRejected additionally returns the actual incoming C. WriteFailure has no 40 # incoming owner. These results do not themselves commit or finish the transaction. 41 type SetResult is Type: 42 Written{tx: Tx} 43 WriteRejected{tx: Tx,incoming: C,error: W.WorldError} 44 WriteFailure{tx: Tx,error: W.WorldError} 45 # Transactional removal result retaining Tx on success or refusal. The removed 46 # payload remains in the inverse journal for rollback rather than being returned. 47 type RemoveResult is Type: 48 Removed{tx: Tx} 49 RemoveRejected{tx: Tx,error: W.WorldError} 50 51 # Define trusted column/projection plumbing for this family. This does not insert 52 # a component, validate an entity or register a gameplay system. Ordinary declaration 53 # providers expose only the appropriate opaque-owner capabilities to gameplay. 54 def define(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,name: String) -> Family: 55 Family{name} 56 57 def access(~V: Data,value: Maybe<&2,V>) -> Access: 58 match value: 59 case None{}: ComponentAbsent{} 60 case Some{value}: Found{value} 61 62 def get_projected(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,rest: Rest,observed: Col.Column & Maybe<&2,V>) -> Store & Access: 63 (column,value) = observed 64 (put(rest,column),access(~V,value)) 65 66 def get_store(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,split: Rest & Col.Column) -> Store & Access: 67 (rest,column) = split 68 get_projected(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,rest,Col.view(~S,~C,~V,~project,column,id)) 69 70 def get_world(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,world: W.World) -> W.World & Access: 71 match world: 72 case W.World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock}: 73 W.with_store_finish(~S,~Store,~R,~E,~Access,get_store(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,take(store)),namespace,allocator,resource,events,pending,registrations,nextSystemId,clock) 74 75 def get_allowed(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,checked: W.World & Bool,+handle: W.Handle) -> W.World & Access: 76 match checked handle: 77 case (world,False{}) _: (world,MissingEntity{}) 78 case (world,True{}) W.Handle{_,K.EntityKey{+id,_}}: 79 get_world(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,world) 80 81 # Read a detached view while returning World and the actual projected payload to 82 # its column. Validate namespace, generation and live state before column access; 83 # invalid/stale/foreign handles yield MissingEntity, not ComponentAbsent. 84 # See examples/health-decay/schema.bend for family provisioning. 85 def get(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,family: Family,world: W.World,+handle: W.Handle) -> W.World & Access: 86 get_allowed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,W.valid(~S,~Store,~R,~E,world,handle),handle) 87 88 def has_access(~V: Data,value: Access) -> Access: 89 match value: 90 case MissingEntity{}: MissingEntity{} 91 case ComponentAbsent{}: Found{False{}} 92 case Found{_}: Found{True{}} 93 94 def has_join(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,observed: W.World & Access) -> W.World & Access: 95 (world,value) = observed 96 (world,has_access(~V,value)) 97 98 # Return presence as Found(Bool), preserving MissingEntity for invalid handles. 99 # This delegates to get, so the configured projection is evaluated on a present 100 # payload; it is not a projection-free raw occupancy query. 101 def has(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,family: Family,world: W.World,handle: W.Handle) -> W.World & Access: 102 has_join(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,get(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,world,handle)) 103 104 def owner_present(~C: Type,owner: Maybe<&1,C>) -> Maybe<&1,C> & Bool: 105 match owner: 106 case None{}: (None{},False{}) 107 case Some{owner}: (Some{owner},True{}) 108 109 def mark_swapped(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,rest: Rest,column: Col.Column,+id: U32,incoming: Bool,tick: Tick.Tick,observed: Maybe<&1,C> & Bool) -> Store & Swap: 110 (previous,wasPresent) = observed 111 (put(rest,Col.mark(~S,~C,column,id,incoming,wasPresent,tick)),Swapped{previous}) 112 113 def swap_join(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,rest: Rest,+id: U32,incoming: Bool,tick: Tick.Tick,result: Col.SwapOutcome) -> Store & Swap: 114 match result: 115 case Col.Accepted{column,previous}: mark_swapped(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,rest,column,id,incoming,tick,owner_present(~C,previous)) 116 case Col.Rejected{column,incoming}: (put(rest,column),SwapRejected{incoming}) 117 118 def swap_store(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,incoming: Maybe<&1,C>,incomingPresent: Bool,tick: Tick.Tick,split: Rest & Col.Column) -> Store & Swap: 119 (rest,column) = split 120 swap_join(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,rest,id,incomingPresent,tick,Col.swap(~S,~C,Col.ensure(~S,~C,column,id),id,incoming)) 121 122 def swap_world(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,incoming: Maybe<&1,C>,incomingPresent: Bool,tick: Tick.Tick,world: W.World) -> W.World & Swap: 123 match world: 124 case W.World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock}: 125 W.with_store_finish(~S,~Store,~R,~E,~Swap,swap_store(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,incoming,incomingPresent,tick,take(store)),namespace,allocator,resource,events,pending,registrations,nextSystemId,clock) 126 127 def replaced(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,result: W.World & Swap) -> Replacement,C>: 128 match result: 129 case (world,Swapped{previous}): Replaced{world,previous} 130 case (world,SwapRejected{incoming}): ReplaceRejected{world,incoming,W.CapacityExceeded{}} 131 132 def replace_clock_advanced(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,incomingPresent: Bool,incoming: Maybe<&1,C>,advanced: W.World & Tick.Tick) -> Replacement,C>: 133 (world,+tick) = advanced 134 replaced(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,swap_world(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,incoming,incomingPresent,tick,world)) 135 136 def replace_present(~S:Data,~Store:Type,~R:Type,~E:Data,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,world:W.World,id:U32,observed:Maybe<&1,C> & Bool) -> Replacement,C>: 137 match observed: 138 case (incoming,present):replace_clock_advanced(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,present,incoming,W.write_clock(~S,~Store,~R,~E,world)) 139 140 def replace_allowed(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,checked: W.World & Bool,+handle: W.Handle,incoming: Maybe<&1,C>) -> Replacement,C>: 141 match checked handle: 142 case (world,False{}) _: ReplaceRejected{world,incoming,W.MissingEntity{}} 143 case (world,True{}) W.Handle{_,K.EntityKey{+id,_}}: 144 replace_present(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,world,id,owner_present(~C,incoming)) 145 146 # Ordinary writes require a live entity. Foreign handles are rejected before 147 # any column access, size computation, allocation or owned replacement move. 148 # Immediately insert/replace with Some(C), or remove with None, on a live handle. 149 # Return displaced C on success and incoming C on rejection; capacity errors 150 # remain explicit. This operation is not deferred and has no automatic undo journal. 151 def replace(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,family: Family,world: W.World,+handle: W.Handle,incoming: Maybe<&1,C>) -> Replacement,C>: 152 replace_allowed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,W.valid(~S,~Store,~R,~E,world,handle),handle,incoming) 153 154 # Deferred creation may populate reserved entities before activation. This is 155 # provisioning/structural command machinery, not a gameplay capability. 156 # Trusted structural adapter permitting metadata-valid reserved entities as well 157 # as live ones. Retains replacement owners/results like replace; this is not a 158 # public gameplay bypass for accessing an arbitrary inactive entity. 159 def structural_replace(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,family: Family,world: W.World,+handle: W.Handle,incoming: Maybe<&1,C>) -> Replacement,C>: 160 replace_allowed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,W.metadata_valid(~S,~Store,~R,~E,world,handle),handle,incoming) 161 162 def restore_done(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,result: Replacement,C>) -> W.World: 163 match result: 164 case Replaced{world,_}: world 165 case ReplaceRejected{world,_,_}: world 166 167 # Inverse closures own their old component, not a copied payload. Replaying 168 # drops the replaced current owner exactly once and restores the journal owner. 169 def restore_swap(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,rest: Rest,result: Col.SwapOutcome) -> Store & Unit: 170 match result: 171 case Col.Accepted{column,_}: (put(rest,column),Unit{}) 172 case Col.Rejected{column,_}: (put(rest,column),Unit{}) 173 174 def restore_store(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,incoming: Maybe<&1,C>,split: Rest & Col.Column) -> Store & Unit: 175 (rest,column) = split 176 restore_swap(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,rest,Col.swap(~S,~C,Col.ensure(~S,~C,column,id),id,incoming)) 177 178 def restore_world(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,incoming: Maybe<&1,C>,world: W.World) -> W.World & Unit: 179 match world: 180 case W.World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock}: 181 W.with_store_finish(~S,~Store,~R,~E,~Unit,restore_store(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,incoming,take(store)),namespace,allocator,resource,events,pending,registrations,nextSystemId,clock) 182 183 def restore_observed(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,observed: W.World & Unit) -> W.World: 184 (world,_) = observed 185 world 186 187 def restore_allowed(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,checked: W.World & Bool,handle: W.Handle,incoming: Maybe<&1,C>) -> W.World: 188 match checked handle: 189 case (world,False{}) _: world 190 case (world,True{}) W.Handle{_,K.EntityKey{+id,_}}: restore_observed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,restore_world(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,incoming,world)) 191 192 def restore(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,family: Family,+handle: W.Handle,incoming: Maybe<&1,C>,world: W.World) -> W.World: 193 restore_allowed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,W.metadata_valid(~S,~Store,~R,~E,world,handle),handle,incoming) 194 195 def lifecycle_join(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,rest: Rest,observed: Col.Column & L.Stamp) -> Store & L.Stamp: 196 (column,stamp) = observed 197 (put(rest,column),stamp) 198 199 def lifecycle_store(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,split: Rest & Col.Column) -> Store & L.Stamp: 200 (rest,column) = split 201 lifecycle_join(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,rest,Col.lifecycle(~S,~C,column,id)) 202 203 def lifecycle_world(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,world: W.World) -> W.World & L.Stamp: 204 match world: 205 case W.World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock}: 206 W.with_store_finish(~S,~Store,~R,~E,~L.Stamp,lifecycle_store(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,take(store)),namespace,allocator,resource,events,pending,registrations,nextSystemId,clock) 207 208 def lifecycle_allowed(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,checked: W.World & Bool,handle: W.Handle) -> W.World & L.Stamp: 209 match checked handle: 210 case (world,False{}) _: (world,L.Stamp{None{},None{}}) 211 case (world,True{}) W.Handle{_,K.EntityKey{+id,_}}: lifecycle_world(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,world) 212 213 # Read this component slot's lifecycle stamp while returning World. Invalid live 214 # handle validation yields Stamp(None,None); no detached component payload is returned. 215 def lifecycle(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,family: Family,world: W.World,+handle: W.Handle) -> W.World & L.Stamp: 216 lifecycle_allowed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,W.valid(~S,~Store,~R,~E,world,handle),handle) 217 218 def restore_stamp_store(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,stamp: L.Stamp,split: Rest & Col.Column) -> Store & Unit: 219 (rest,column) = split 220 (put(rest,Col.restore_stamp(~S,~C,column,id,stamp)),Unit{}) 221 222 def restore_stamp_world(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id: U32,stamp: L.Stamp,world: W.World) -> W.World & Unit: 223 match world: 224 case W.World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock}: 225 W.with_store_finish(~S,~Store,~R,~E,~Unit,restore_stamp_store(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,stamp,take(store)),namespace,allocator,resource,events,pending,registrations,nextSystemId,clock) 226 227 def restore_stamp_done(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,observed: W.World & Unit) -> W.World: 228 (world,_) = observed 229 world 230 231 def restore_stamp(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+handle: W.Handle,stamp: L.Stamp,world: W.World) -> W.World: 232 match handle: 233 case W.Handle{_,K.EntityKey{+id,_}}: restore_stamp_done(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,restore_stamp_world(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,stamp,world)) 234 235 def tx_get_join(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,observed: W.World & Access) -> T.Tx,E> & Access: 236 (world,value) = observed 237 (T.Tx{world,undo,commands,events,trace},value) 238 239 # Read through the current Tx World, preserving its journal, commands, events and 240 # trace. Returns the same Access distinctions as get; reads do not finish the Tx. 241 def tx_get(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,family: Family,tx: T.Tx,E>,handle: W.Handle) -> T.Tx,E> & Access: 242 match tx: 243 case T.Tx{world,undo,commands,events,trace}: 244 tx_get_join(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,undo,commands,events,trace,get(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,world,handle)) 245 246 def trace_error(error:W.WorldError) -> String: 247 match error: 248 case W.MissingEntity{}:"MissingEntity" 249 case W.CapacityExceeded{}:"CapacityExceeded" 250 case W.NamespaceExhausted{}:"NamespaceExhausted" 251 case W.SystemIdExhausted{}:"SystemIdExhausted" 252 def trace_handle(~S:Data,handle:W.Handle) -> String: 253 match handle: 254 case W.Handle{namespace,key}:U32.show(namespace) ++ ":" ++ KeyRender.show(key) 255 def trace_write(~S:Data,state:TraceFacts.State,name:String,handle:W.Handle,result:TraceFacts.FactResult) -> TraceFacts.State: 256 match state: 257 case TraceFacts.Off{}:TraceFacts.Off{} 258 case TraceFacts.On{facts}:TraceFacts.On{TraceFacts.Fact{TraceFacts.ComponentValue{},name,TraceFacts.WriteOperation{},Some{trace_handle(~S,handle)},TraceFacts.OpaqueValue{},TraceFacts.OpaqueValue{},result} <> facts} 259 def trace_refusal(~S:Data,state:TraceFacts.State,name:String,handle:W.Handle,error:W.WorldError) -> TraceFacts.State: 260 match state: 261 case TraceFacts.Off{}:TraceFacts.Off{} 262 case TraceFacts.On{facts}:TraceFacts.On{TraceFacts.Fact{TraceFacts.ComponentValue{},name,TraceFacts.WriteOperation{},Some{trace_handle(~S,handle)},TraceFacts.OpaqueValue{},TraceFacts.OpaqueValue{},TraceFacts.Refused{trace_error(error)}} <> facts} 263 def family_name(~S:Data,~Store:Type,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,family:Family) -> String: 264 match family: 265 case Family{name}:name 266 def tx_written(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+family: Family,+handle: W.Handle,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,+stamp: L.Stamp,result: Replacement,C>) -> SetResult,E>,C>: 267 match result: 268 case Replaced{world,previous}: 269 Written{T.Tx{world,(world => restore_stamp(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,handle,stamp,restore(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,handle,previous,world))) <> undo,commands,events,trace_write(~S,trace,family_name(~S,~Store,~Rest,~C,~V,~Token,~take,~put,~project,family),handle,TraceFacts.Attempted{})}} 270 case ReplaceRejected{world,Some{incoming},+error}: 271 WriteRejected{T.Tx{world,undo,commands,events,trace_refusal(~S,trace,family_name(~S,~Store,~Rest,~C,~V,~Token,~take,~put,~project,family),handle,error)},incoming,error} 272 case ReplaceRejected{world,None{},+error}: 273 WriteFailure{T.Tx{world,undo,commands,events,trace_refusal(~S,trace,family_name(~S,~Store,~Rest,~C,~V,~Token,~take,~put,~project,family),handle,error)},error} 274 275 def tx_set_stamped(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+family: Family,+handle: W.Handle,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,incoming: C,observed: W.World & L.Stamp) -> SetResult,E>,C>: 276 (world,stamp) = observed 277 tx_written(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,handle,undo,commands,events,trace,stamp,replace(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,world,handle,Some{incoming})) 278 279 def stamped_store_join(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,stamp:L.Stamp,result:Store & Swap) -> Store & (Swap & L.Stamp): 280 match result: 281 case (store,outcome): (store,(outcome,stamp)) 282 283 def stamped_column(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,rest:Rest,+id:U32,incoming:C,+tick:Tick.Tick,allowed:Bool,observed:Col.Column & L.Stamp) -> Store & (Swap & L.Stamp): 284 match allowed observed: 285 case False{}, (column,stamp): (put(rest,column),(SwapRejected{Some{incoming}},stamp)) 286 case True{}, (column,stamp): stamped_store_join(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,stamp,swap_join(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,rest,id,True{},tick,Col.swap(~S,~C,Col.ensure(~S,~C,column,id),id,Some{incoming}))) 287 288 def stamped_store(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+id:U32,incoming:C,+tick:Tick.Tick,allowed:Bool,split:Rest & Col.Column) -> Store & (Swap & L.Stamp): 289 match split: 290 case (rest,column): stamped_column(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,rest,id,incoming,tick,allowed,Col.lifecycle(~S,~C,column,id)) 291 292 def stamped_replaced(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,observed:W.World & (Swap & L.Stamp)) -> Replacement,C> & L.Stamp: 293 match observed: 294 case (world,(Swapped{previous},stamp)): (Replaced{world,previous},stamp) 295 case (world,(SwapRejected{incoming},stamp)): (ReplaceRejected{world,incoming,W.CapacityExceeded{}},stamp) 296 297 def stamped_clocked(~S:Data,~Store:Type,~R:Type,~E:Data,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,world:W.World,id:U32,incoming:C,tick:Tick.Tick) -> Replacement,C> & L.Stamp: 298 match world: 299 case W.World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock}: 300 stamped_replaced(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,W.with_store_finish(~S,~Store,~R,~E,~(Swap & L.Stamp),stamped_store(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,incoming,tick,True{},take(store)),namespace,allocator,resource,events,pending,registrations,nextSystemId,clock)) 301 302 def stamped_advanced(~S:Data,~Store:Type,~R:Type,~E:Data,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,id:U32,incoming:C,advanced:W.World & Tick.Tick) -> Replacement,C> & L.Stamp: 303 (world,tick)=advanced 304 stamped_clocked(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,world,id,incoming,tick) 305 306 def stamped_world(~S:Data,~Store:Type,~R:Type,~E:Data,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,id:U32,incoming:C,world:W.World) -> Replacement,C> & L.Stamp: 307 stamped_advanced(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,incoming,W.write_clock(~S,~Store,~R,~E,world)) 308 309 def stamped_allowed(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,checked:W.World & Bool,handle:W.Handle,incoming:C) -> Replacement,C> & L.Stamp: 310 match checked handle: 311 case (world,False{}) _: (ReplaceRejected{world,Some{incoming},W.MissingEntity{}},L.Stamp{None{},None{}}) 312 case (world,True{}) W.Handle{_,K.EntityKey{+id,_}}: stamped_world(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,id,incoming,world) 313 314 def tx_set_fused(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+family:Family,+handle:W.Handle,undo:List<(W.World -> W.World)>,commands:List>>,events:List<&2,E>,trace:TraceFacts.State,observed:Replacement,C> & L.Stamp) -> SetResult,E>,C>: 315 match observed: 316 case (result,stamp): tx_written(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,handle,undo,commands,events,trace,stamp,result) 317 318 # Write an actual possibly-affine Type payload into the current Tx World and 319 # journal the old payload and stamp for rollback. Successful writes are visible 320 # within this Tx immediately; finish decides commit versus inverse replay. Refusal 321 # returns the incoming owner through WriteRejected. Ordinary rollback is not a 322 # receipt returning every newly written payload; use explicit recovery APIs for that. 323 def tx_set(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+family: Family,tx: T.Tx,E>,+handle: W.Handle,incoming: C) -> SetResult,E>,C>: 324 match tx: 325 case T.Tx{world,undo,commands,events,trace}: 326 tx_set_fused(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,handle,undo,commands,events,trace,stamped_allowed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,W.valid(~S,~Store,~R,~E,world,handle),handle,incoming)) 327 328 def tx_removed(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+family: Family,+handle: W.Handle,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,+stamp: L.Stamp,result: Replacement,C>) -> RemoveResult,E>>: 329 match result: 330 case Replaced{world,previous}: 331 Removed{T.Tx{world,(world => restore_stamp(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,handle,stamp,restore(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,handle,previous,world))) <> undo,commands,events,trace}} 332 case ReplaceRejected{world,_,error}: 333 RemoveRejected{T.Tx{world,undo,commands,events,trace},error} 334 335 def tx_remove_stamped(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+family: Family,+handle: W.Handle,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,observed: W.World & L.Stamp) -> RemoveResult,E>>: 336 (world,stamp) = observed 337 tx_removed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,handle,undo,commands,events,trace,stamp,replace(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,world,handle,None{})) 338 339 # Remove from a live entity within Tx, journaling the previous owner/stamp for 340 # rollback. Removing an already absent component can succeed; invalid handles or 341 # capacity refusal return RemoveRejected. Finish is still the caller's choice. 342 def tx_remove(~S: Data,~Store: Type,~R: Type,~E: Data,~Rest: Type,~C: Type,~V: Data,~Token: Data,~take: Store -> Rest & Col.Column,~put: Rest -> Col.Column -> Store,~project: C -> C & V,+family: Family,tx: T.Tx,E>,+handle: W.Handle) -> RemoveResult,E>>: 343 match tx: 344 case T.Tx{world,undo,commands,events,trace}: 345 tx_remove_stamped(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,handle,undo,commands,events,trace,lifecycle(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,world,handle)) 346 347 def displayed_access(~V:Data,~display:V -> String,value:Access) -> TraceFacts.Value: 348 match value: 349 case Found{value}:TraceFacts.DisplayedValue{display(value)} 350 case ComponentAbsent{}:TraceFacts.UnavailableValue{} 351 case MissingEntity{}:TraceFacts.UnavailableValue{} 352 def displayed_written(~S:Data,~Store:Type,~R:Type,~E:Data,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,before:TraceFacts.Value,after:TraceFacts.Value,result:SetResult,E>,C>) -> SetResult,E>,C>: 353 match result: 354 case Written{tx}:Written{T.annotate_newest(~W.World,~E,tx,before,after)} 355 case WriteRejected{tx,incoming,error}:WriteRejected{T.annotate_newest(~W.World,~E,tx,before,after),incoming,error} 356 case WriteFailure{tx,error}:WriteFailure{T.annotate_newest(~W.World,~E,tx,before,after),error} 357 def displayed_viewed(~S:Data,~Store:Type,~R:Type,~E:Data,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,~display:V -> String,family:Family,handle:W.Handle,incoming:C & V,observed:T.Tx,E> & Access) -> SetResult,E>,C>: 358 match incoming observed: 359 case (incoming,after), (tx,before):displayed_written(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,displayed_access(~V,~display,before),TraceFacts.DisplayedValue{display(after)},tx_set(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,tx,handle,incoming)) 360 def displayed_enabled(~S:Data,~Store:Type,~R:Type,~E:Data,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,~display:V -> String,+family:Family,+handle:W.Handle,incoming:C,observed:T.Tx,E> & Bool) -> SetResult,E>,C>: 361 match observed: 362 case (tx,False{}):tx_set(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,tx,handle,incoming) 363 case (tx,True{}):displayed_viewed(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,~display,family,handle,project(incoming),tx_get(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,family,tx,handle)) 364 # Trace-aware tx_set: when tracing is enabled, project the current and incoming 365 # values and render detached V strings to annotate the attempted write/refusal. 366 # Disabled tracing delegates directly without these presentation reads. Ownership 367 # and write results remain those of tx_set; project/display are trusted adapters. 368 def tx_set_displayed(~S:Data,~Store:Type,~R:Type,~E:Data,~Rest:Type,~C:Type,~V:Data,~Token:Data,~take:Store -> Rest & Col.Column,~put:Rest -> Col.Column -> Store,~project:C -> C & V,~display:V -> String,family:Family,tx:T.Tx,E>,handle:W.Handle,incoming:C) -> SetResult,E>,C>: 369 displayed_enabled(~S,~Store,~R,~E,~Rest,~C,~V,~Token,~take,~put,~project,~display,family,handle,incoming,T.trace_enabled(~W.World,~E,tx))