bundle_batch.bend
bendvy-ecs-core@0.1.0.1 · 114 lines · 0 definitions
1import Base
2import bendvy-ecs-core@0.1.0.1/deferred_command.bend as Deferred
3import bendvy-ecs-core@0.1.0.1/trace_facts.bend as TraceFacts
4import entity_key.bend as K
5import bundle_delivery.bend as B
6import world.bend as W
7import transaction.bend as T
8import commands.bend as Cmd
9
10# Prepared payload owners remain typed until successful body completion.
11# This insert-only journal introduces no reservation/cancellation policy.
12type Prepared<S:Data,P:Type> is Type:
13 Prepared{target:W.Handle<S>,payload:P,position:Nat}
14 ReservationReceipt{target:W.Handle<S>}
15type Batch<S:Data,Store:Type,R:Type,E:Data,P:Type> is Type:
16 Batch{tx:T.Tx<W.World<S,Store,R,E>,E>,prepared:List<Prepared<S,P>>}
17type Inserted<S:Data,Store:Type,R:Type,E:Data,P:Type> is Type:
18 Inserted{batch:Batch<S,Store,R,E,P>}
19 Refused{batch:Batch<S,Store,R,E,P>,payload:P,error:W.WorldError}
20type Spawned<S:Data,Store:Type,R:Type,E:Data,P:Type> is Type:
21 Spawned{batch:Batch<S,Store,R,E,P>,target:W.Handle<S>}
22 SpawnRefused{batch:Batch<S,Store,R,E,P>,payload:P,error:W.WorldError}
23type Finished<S:Data,Store:Type,R:Type,E:Data,P:Type,Error:Data> is Type:
24 Committed{world:W.World<S,Store,R,E>}
25 Failed{world:W.World<S,Store,R,E>,payloads:List<P>,error:Error}
26def begin(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,tx:T.Tx<W.World<S,Store,R,E>,E>) -> Batch<S,Store,R,E,P>:
27 Batch{tx,[]}
28# Count returns the very same affine closures in the same reverse queue order.
29def counted(~World:Type,command:Deferred.Command<World>,result:List<Deferred.Command<World>> & Nat) -> List<Deferred.Command<World>> & Nat:
30 match result:
31 case (commands,count): (command <> commands,1n+count)
32def count_commands(~World:Type,commands:List<Deferred.Command<World>>) -> List<Deferred.Command<World>> & Nat:
33 match commands:
34 case []: ([],0n)
35 case command <> tail: counted(~World,command,count_commands(~World,tail))
36def positioned(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,prepared:List<Prepared<S,P>>,world:W.World<S,Store,R,E>,undo:List<(W.World<S,Store,R,E> -> W.World<S,Store,R,E>)>,events:List<&2,E>,trace:TraceFacts.State,target:W.Handle<S>,payload:P,result:List<Deferred.Command<W.World<S,Store,R,E>>> & Nat) -> Inserted<S,Store,R,E,P>:
37 match result:
38 case (commands,position): Inserted{Batch{T.Tx{world,undo,commands,events,trace},Prepared{target,payload,position} <> prepared}}
39# Receipt rows are created only from this Batch's actual Cmd.tx_spawn result.
40# Public gameplay cannot construct the opaque Batch; raw engine constructors
41# and provider callbacks remain explicitly trusted setup boundaries.
42def receipt_join(~S:Data,~P:Type,entry:Prepared<S,P>,seen:Bool,result:List<Prepared<S,P>> & Bool) -> List<Prepared<S,P>> & Bool:
43 match result:
44 case (tail,found): (entry <> tail,Bool.or(seen,found))
45def receipt_find(~S:Data,~P:Type,prepared:List<Prepared<S,P>>,+target:W.Handle<S>) -> List<Prepared<S,P>> & Bool:
46 match prepared:
47 case []: ([],False{})
48 case Prepared{handle,payload,position} <> tail: receipt_join(~S,~P,Prepared{handle,payload,position},False{},receipt_find(~S,~P,tail,target))
49 case ReservationReceipt{W.Handle{+ns,+id}} <> tail:
50 match target:
51 case W.Handle{+otherNs,+otherId}: receipt_join(~S,~P,ReservationReceipt{W.Handle{ns,id}},Bool.and(U32.is_eq(ns,otherNs),K.equal(id,otherId)),receipt_find(~S,~P,tail,W.Handle{otherNs,otherId}))
52def receipt_checked(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,world:W.World<S,Store,R,E>,undo:List<(W.World<S,Store,R,E> -> W.World<S,Store,R,E>)>,commands:List<Deferred.Command<W.World<S,Store,R,E>>>,events:List<&2,E>,trace:TraceFacts.State,target:W.Handle<S>,payload:P,result:List<Prepared<S,P>> & Bool) -> Inserted<S,Store,R,E,P>:
53 match result:
54 case (prepared,False{}): Refused{Batch{T.Tx{world,undo,commands,events,trace},prepared},payload,W.MissingEntity{}}
55 case (prepared,True{}): positioned(~S,~Store,~R,~E,~P,prepared,world,undo,events,trace,target,payload,count_commands(~W.World<S,Store,R,E>,commands))
56def checked(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,prepared:List<Prepared<S,P>>,undo:List<(W.World<S,Store,R,E> -> W.World<S,Store,R,E>)>,commands:List<Deferred.Command<W.World<S,Store,R,E>>>,events:List<&2,E>,trace:TraceFacts.State,+target:W.Handle<S>,payload:P,observed:W.World<S,Store,R,E> & Bool) -> Inserted<S,Store,R,E,P>:
57 match observed:
58 case (world,False{}): receipt_checked(~S,~Store,~R,~E,~P,world,undo,commands,events,trace,target,payload,receipt_find(~S,~P,prepared,target))
59 case (world,True{}): positioned(~S,~Store,~R,~E,~P,prepared,world,undo,events,trace,target,payload,count_commands(~W.World<S,Store,R,E>,commands))
60def insert(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,batch:Batch<S,Store,R,E,P>,+target:W.Handle<S>,payload:P) -> Inserted<S,Store,R,E,P>:
61 match batch:
62 case Batch{T.Tx{world,undo,commands,events,trace},prepared}: checked(~S,~Store,~R,~E,~P,prepared,undo,commands,events,trace,target,payload,W.valid(~S,~Store,~R,~E,world,target))
63# Replay only closures preceding this authored insertion. Position is generated
64# by count_commands, not supplied by gameplay; raw constructors remain trusted.
65def preceding(~World:Type,~E:Data,count:Nat,commands:List<Deferred.Command<World>>,tx:T.Tx<World,E>) -> List<Deferred.Command<World>> & T.Tx<World,E>:
66 match count commands:
67 case 0n _: (commands,tx)
68 case 1n+rest Nil{}: ([],tx)
69 case 1n+rest Con{command,tail}: preceding(~World,~E,rest,tail,T.stage_command(~World,~E,tx,command))
70def remaining(~World:Type,~E:Data,commands:List<Deferred.Command<World>>,tx:T.Tx<World,E>) -> T.Tx<World,E>:
71 match commands:
72 case []: tx
73 case command <> tail: remaining(~World,~E,tail,T.stage_command(~World,~E,tx,command))
74def staged_after(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,~attempt:W.World<S,Store,R,E> -> W.Handle<S> -> P -> B.Delivery<W.World<S,Store,R,E>,P>,~recover:W.World<S,Store,R,E> -> P -> W.WorldError -> W.World<S,Store,R,E>,target:W.Handle<S>,payload:P,next:List<Deferred.Command<W.World<S,Store,R,E>>> -> T.Tx<W.World<S,Store,R,E>,E> -> T.Tx<W.World<S,Store,R,E>,E>,result:List<Deferred.Command<W.World<S,Store,R,E>>> & T.Tx<W.World<S,Store,R,E>,E>) -> T.Tx<W.World<S,Store,R,E>,E>:
75 match result:
76 case (after,before): next(after,B.enqueue(~S,~Store,~R,~E,~P,~attempt,~recover,before,target,payload))
77def staged(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,~attempt:W.World<S,Store,R,E> -> W.Handle<S> -> P -> B.Delivery<W.World<S,Store,R,E>,P>,~recover:W.World<S,Store,R,E> -> P -> W.WorldError -> W.World<S,Store,R,E>,prepared:List<Prepared<S,P>>,commands:List<Deferred.Command<W.World<S,Store,R,E>>>,+current:Nat,tx:T.Tx<W.World<S,Store,R,E>,E>) -> T.Tx<W.World<S,Store,R,E>,E>:
78 match prepared:
79 case []: remaining(~W.World<S,Store,R,E>,~E,commands,tx)
80 case ReservationReceipt{target} <> tail: staged(~S,~Store,~R,~E,~P,~attempt,~recover,tail,commands,current,tx)
81 case Prepared{+target,payload,+position} <> tail:
82 staged_after(~S,~Store,~R,~E,~P,~attempt,~recover,target,payload,after => before => staged(~S,~Store,~R,~E,~P,~attempt,~recover,tail,after,position,before),preceding(~W.World<S,Store,R,E>,~E,Nat.sub(position,current),commands,tx))
83def stage(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,~attempt:W.World<S,Store,R,E> -> W.Handle<S> -> P -> B.Delivery<W.World<S,Store,R,E>,P>,~recover:W.World<S,Store,R,E> -> P -> W.WorldError -> W.World<S,Store,R,E>,prepared:List<Prepared<S,P>>,tx:T.Tx<W.World<S,Store,R,E>,E>) -> T.Tx<W.World<S,Store,R,E>,E>:
84 match tx:
85 case T.Tx{world,undo,commands,events,trace}: staged(~S,~Store,~R,~E,~P,~attempt,~recover,prepared,List.reverse(&1,Deferred.Command<W.World<S,Store,R,E>>,commands),0n,T.Tx{world,undo,[],events,trace})
86def originals(~S:Data,~P:Type,prepared:List<Prepared<S,P>>) -> List<P>:
87 match prepared:
88 case []: []
89 case ReservationReceipt{target} <> tail: originals(~S,~P,tail)
90 case Prepared{target,payload,position} <> tail: payload <> originals(~S,~P,tail)
91def committed(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,~Error:Data,observed:T.Returned<W.World<S,Store,R,E>,Error>) -> Finished<S,Store,R,E,P,Error>:
92 match observed:
93 case T.Returned{world,outcome}: Committed{world}
94def failed(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,~Error:Data,payloads:List<P>,error:Error,observed:T.Returned<W.World<S,Store,R,E>,Error>) -> Finished<S,Store,R,E,P,Error>:
95 match observed:
96 case T.Returned{world,outcome}: Failed{world,payloads,error}
97def finish(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,~Error:Data,~attempt:W.World<S,Store,R,E> -> W.Handle<S> -> P -> B.Delivery<W.World<S,Store,R,E>,P>,~recover:W.World<S,Store,R,E> -> P -> W.WorldError -> W.World<S,Store,R,E>,batch:Batch<S,Store,R,E,P>,outcome:T.Outcome<Error>) -> Finished<S,Store,R,E,P,Error>:
98 match batch outcome:
99 case Batch{tx,prepared} T.Success{}: committed(~S,~Store,~R,~E,~P,~Error,Cmd.tx_finish(~S,~Store,~R,~E,~Error,stage(~S,~Store,~R,~E,~P,~attempt,~recover,List.reverse(&1,Prepared<S,P>,prepared),tx),T.Success{}))
100 case Batch{tx,prepared} T.Failure{+error}: failed(~S,~Store,~R,~E,~P,~Error,originals(~S,~P,List.reverse(&1,Prepared<S,P>,prepared)),error,Cmd.tx_finish(~S,~Store,~R,~E,~Error,tx,T.Failure{error}))
101
102def empty_population(~S:Data,~Store:Type,~R:Type,~E:Data,world:W.World<S,Store,R,E>,handle:W.Handle<S>,unit:Unit) -> W.World<S,Store,R,E>:
103 world
104def spawn_positioned(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,target:W.Handle<S>,result:Inserted<S,Store,R,E,P>) -> Spawned<S,Store,R,E,P>:
105 match result:
106 case Inserted{batch}: Spawned{batch,target}
107 case Refused{batch,payload,error}: SpawnRefused{batch,payload,error}
108def spawn_reserved(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,prepared:List<Prepared<S,P>>,payload:P,result:T.Tx<W.World<S,Store,R,E>,E> & W.Reservation<S>) -> Spawned<S,Store,R,E,P>:
109 match result:
110 case (T.Tx{world,undo,commands,events,trace},W.Reserved{+target}): spawn_positioned(~S,~Store,~R,~E,~P,target,positioned(~S,~Store,~R,~E,~P,ReservationReceipt{target} <> prepared,world,undo,events,trace,target,payload,count_commands(~W.World<S,Store,R,E>,commands)))
111 case (tx,W.ReservationRejected{error}): SpawnRefused{Batch{tx,prepared},payload,error}
112def spawn(~S:Data,~Store:Type,~R:Type,~E:Data,~P:Type,batch:Batch<S,Store,R,E,P>,payload:P) -> Spawned<S,Store,R,E,P>:
113 match batch:
114 case Batch{tx,prepared}: spawn_reserved(~S,~Store,~R,~E,~P,prepared,payload,Cmd.tx_spawn(~S,~Store,~R,~E,~Unit,~(world => handle => unit => world),tx,Unit{}))