1 import Base 2 import ./transaction.bend as T 3 import ./bundle_install.bend as Install 4 import ./world.bend as W 5 import ./bundle_batch.bend as Batch 6 7 # Explicit app-owned reversible receipts live outside the World journal. 8 # Their generated position preserves ordering with ordinary resource/component 9 # inverses. Recovered affine owners are returned to the Local adapter, never 10 # placed into a resource that a subsequent inverse could overwrite. 11 type Receipt is Type: 12 Receipt{position:Nat,inverse:World -> Install.Restore} 13 type Pending is Type:Pending{owner:Q,error:W.WorldError} 14 type Returned is Type: 15 Returned{world:World,owners:List

,pending:List>} 16 17 def stepped(~World:Type,count:Nat,world:World,undo:List<(World -> World)>) -> World & List<(World -> World)>: 18 match count undo: 19 case 0n _:(world,undo) 20 case 1n+rest Nil{}:(world,[]) 21 case 1n+rest Con{inverse,tail}:stepped(~World,rest,inverse(world),tail) 22 23 def restored(~World:Type,~P:Type,~Q:Type,next:World -> List

-> List> -> Returned,owners:List

,pending:List>,result:Install.Restore) -> Returned: 24 match result: 25 case Install.Restored{world,owner}:next(world,owner <> owners,pending) 26 case Install.Pending{world,owner,error}:next(world,owners,Pending{owner,error} <> pending) 27 28 def unwound(~World:Type,~P:Type,~Q:Type,inverse:World -> Install.Restore,next:List<(World -> World)> -> World -> List

-> List> -> Returned,owners:List

,pending:List>,result:World & List<(World -> World)>) -> Returned: 29 match result: 30 case (world,undo):restored(~World,~P,~Q,(world => owners => pending => next(undo,world,owners,pending)),owners,pending,inverse(world)) 31 32 def rollback(~World:Type,~P:Type,~Q:Type,receipts:List>,undo:List<(World -> World)>,count:Nat,world:World,owners:List

,pending:List>) -> Returned: 33 match receipts: 34 case []:Returned{T.rollback(~World,undo,world),List.reverse(&1,P,owners),List.reverse(&1,Pending,pending)} 35 case Receipt{+position,inverse} <> rest:unwound(~World,~P,~Q,inverse,(undo => world => owners => pending => rollback(~World,~P,~Q,rest,undo,position,world,owners,pending)),owners,pending,stepped(~World,Nat.sub(count,position),world,undo)) 36 37 def counted(~World:Type,~P:Type,~Q:Type,receipts:List>,world:World,result:List<(World -> World)> & Nat) -> Returned: 38 match result: 39 case (undo,count):rollback(~World,~P,~Q,receipts,undo,count,world,[],[]) 40 41 def inverse_counted(~World:Type,inverse:World -> World,result:List<(World -> World)> & Nat) -> List<(World -> World)> & Nat: 42 match result: 43 case (undo,count):(inverse <> undo,1n+count) 44 def count_inverses(~World:Type,undo:List<(World -> World)>) -> List<(World -> World)> & Nat: 45 match undo: 46 case []:([],0n) 47 case inverse <> rest:inverse_counted(~World,inverse,count_inverses(~World,rest)) 48 def abort(~World:Type,~E:Data,~P:Type,~Q:Type,receipts:List>,tx:T.Tx) -> Returned: 49 match tx: 50 case T.Tx{world,undo,_,_,_}:counted(~World,~P,~Q,receipts,world,count_inverses(~World,undo))