1 import Base 2 import world.bend as W 3 import system.bend as Sys 4 import machine_handlers.bend as H 5 import machine.bend as M 6 7 # Selection metadata has nominal schema/machine indexes; actual registry is affine. 8 type Selector is Data: 9 ExitFrom{value:V} 10 TransitionPair{from:V,to:V} 11 EnterTo{value:V} 12 type Scheduled -> H.Invocation -> Sys.Outcome,String,Unit>> is Type: 13 Scheduled{selector:Selector,requirements:List<&2,Q>,entry:H.Entry} 14 type Tree -> H.Invocation -> Sys.Outcome,String,Unit>> is Type: 15 Empty{} 16 Single{scheduled:Scheduled} 17 Join{left:Tree,right:Tree} 18 type Bundle -> H.Invocation -> Sys.Outcome,String,Unit>> is Type: 19 Bundle{namespace:U32,entries:List<&1,Scheduled>} 20 type Choice is Data: 21 Kept{} 22 SelectedExit{} 23 SelectedTransition{} 24 SelectedEnter{} 25 type Position is Data: 26 Position{selector:Selector,requirements:List<&2,Q>,choice:Choice} 27 type Partition -> H.Invocation -> Sys.Outcome,String,Unit>> is Type: 28 Partition{positions:List<&2,Position>,kept:List<&1,H.Entry>,owners:H.Owners} 29 30 def flattened(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,tree:Tree) -> List<&1,Scheduled>: 31 match tree: 32 case Empty{}: [] 33 case Single{scheduled}: [scheduled] 34 case Join{left,right}: List.append(&1,Scheduled,flattened(~S,~K,~V,~Q,~C,~R,~E,~runner,left),flattened(~S,~K,~V,~Q,~C,~R,~E,~runner,right)) 35 def bundle(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,namespace:U32,tree:Tree) -> Bundle: 36 Bundle{namespace,flattened(~S,~K,~V,~Q,~C,~R,~E,~runner,tree)} 37 38 def contains(~Q:Data,~same:Q -> Q -> Bool,items:List<&2,Q>,+value:Q) -> Bool: 39 match items: 40 case []: False{} 41 case head <> tail: Bool.or(same(head,value),contains(~Q,~same,tail,value)) 42 def collected(~Q:Data,seen:Bool,+prior:List<&2,Q>,value:Q) -> List<&2,Q>: 43 match seen: 44 case True{}: prior 45 case False{}: List.append(&2,Q,prior,[value]) 46 def collect(~Q:Data,~same:Q -> Q -> Bool,items:List<&2,Q>,+prior:List<&2,Q>) -> List<&2,Q>: 47 match items: 48 case []: prior 49 case +head <> tail: collect(~Q,~same,tail,collected(~Q,contains(~Q,~same,prior,head),prior,head)) 50 def requirements_join(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,selector:Selector,+needs:List<&2,Q>,entry:H.Entry,tail:List<&1,Scheduled> & List<&2,Q>) -> List<&1,Scheduled> & List<&2,Q>: 51 (entries,all)=tail 52 (Scheduled{selector,needs,entry} <> entries,List.append(&2,Q,needs,all)) 53 def requirements(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,entries:List<&1,Scheduled>) -> List<&1,Scheduled> & List<&2,Q>: 54 match entries: 55 case []: ([],[]) 56 case Scheduled{selector,+needs,entry} <> tail: requirements_join(~S,~K,~V,~Q,~C,~R,~E,~runner,selector,needs,entry,requirements(~S,~K,~V,~Q,~C,~R,~E,~runner,tail)) 57 58 def exit_choice(yes:Bool) -> Choice: 59 match yes: 60 case True{}: SelectedExit{} 61 case False{}: Kept{} 62 def transition_choice(yes:Bool) -> Choice: 63 match yes: 64 case True{}: SelectedTransition{} 65 case False{}: Kept{} 66 def enter_choice(yes:Bool) -> Choice: 67 match yes: 68 case True{}: SelectedEnter{} 69 case False{}: Kept{} 70 def choose(~S:Data,~K:Data,~V:Data,~same:V -> V -> Bool,selector:Selector,from:V,to:V) -> Choice: 71 match selector: 72 case ExitFrom{value}: exit_choice(same(value,from)) 73 case TransitionPair{old,new}: transition_choice(Bool.and(same(old,from),same(new,to))) 74 case EnterTo{value}: enter_choice(same(value,to)) 75 def placed(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,+choice:Choice,selector:Selector,needs:List<&2,Q>,entry:H.Entry,tail:Partition) -> Partition: 76 match choice tail: 77 case Kept{} Partition{positions,kept,owners}: Partition{Position{selector,needs,choice} <> positions,entry <> kept,owners} 78 case SelectedExit{} Partition{positions,kept,H.Owners{exit,transition,enter}}: Partition{Position{selector,needs,choice} <> positions,kept,H.Owners{entry <> exit,transition,enter}} 79 case SelectedTransition{} Partition{positions,kept,H.Owners{exit,transition,enter}}: Partition{Position{selector,needs,choice} <> positions,kept,H.Owners{exit,entry <> transition,enter}} 80 case SelectedEnter{} Partition{positions,kept,H.Owners{exit,transition,enter}}: Partition{Position{selector,needs,choice} <> positions,kept,H.Owners{exit,transition,entry <> enter}} 81 def partition(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~same:V -> V -> Bool,entries:List<&1,Scheduled>,+from:V,+to:V) -> Partition: 82 match entries: 83 case []: Partition{[],[],H.Owners{[],[],[]}} 84 case Scheduled{+selector,needs,entry} <> tail: placed(~S,~K,~V,~Q,~C,~R,~E,~runner,choose(~S,~K,~V,~same,selector,from,to),selector,needs,entry,partition(~S,~K,~V,~Q,~C,~R,~E,~runner,~same,tail,from,to)) 85 86 def choose_initial(~S:Data,~K:Data,~V:Data,~same:V -> V -> Bool,selector:Selector,to:V) -> Choice: 87 match selector: 88 case EnterTo{value}:enter_choice(same(value,to)) 89 case _:Kept{} 90 def partition_initial(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~same:V -> V -> Bool,entries:List<&1,Scheduled>,+to:V) -> Partition: 91 match entries: 92 case []: Partition{[],[],H.Owners{[],[],[]}} 93 case Scheduled{+selector,needs,entry} <> tail: placed(~S,~K,~V,~Q,~C,~R,~E,~runner,choose_initial(~S,~K,~V,~same,selector,to),selector,needs,entry,partition_initial(~S,~K,~V,~Q,~C,~R,~E,~runner,~same,tail,to)) 94 95 # Recovery explicitly retains all owners even for malformed internal zippers. 96 # Normal public execution constructs its own partition and never accepts a zipper. 97 type Recovery -> H.Invocation -> Sys.Outcome,String,Unit>> is Type: 98 Recovery{entries:List<&1,Scheduled>,positions:List<&2,Position>,kept:List<&1,H.Entry>,owners:H.Owners} 99 type Status is Data: 100 Completed{} 101 HandlerFailed{phase:H.Phase,error:String} 102 MissingRequirements{missing:List<&2,Q>} 103 ForeignBundle{} 104 MachineUnavailable{} 105 type BundleResult -> H.Invocation -> Sys.Outcome,String,Unit>> is Type: 106 Returned{bundle:Bundle,world:W.World,status:Status} 107 InternalOwnerRemainder{namespace:U32,recovery:Recovery,world:W.World,status:Status} 108 109 def recovery_join(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,selector:Selector,needs:List<&2,Q>,entry:H.Entry,tail:Recovery) -> Recovery: 110 match tail: 111 case Recovery{entries,positions,kept,owners}: Recovery{Scheduled{selector,needs,entry} <> entries,positions,kept,owners} 112 def recover(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,positions:List<&2,Position>,kept:List<&1,H.Entry>,owners:H.Owners) -> Recovery: 113 match positions kept owners: 114 case [] kept owners: Recovery{[],[],kept,owners} 115 case Position{selector,needs,Kept{}} <> rest head <> tail owners: recovery_join(~S,~K,~V,~Q,~C,~R,~E,~runner,selector,needs,head,recover(~S,~K,~V,~Q,~C,~R,~E,~runner,rest,tail,owners)) 116 case Position{selector,needs,SelectedExit{}} <> rest kept H.Owners{head <> tail,transition,enter}: recovery_join(~S,~K,~V,~Q,~C,~R,~E,~runner,selector,needs,head,recover(~S,~K,~V,~Q,~C,~R,~E,~runner,rest,kept,H.Owners{tail,transition,enter})) 117 case Position{selector,needs,SelectedTransition{}} <> rest kept H.Owners{exit,head <> tail,enter}: recovery_join(~S,~K,~V,~Q,~C,~R,~E,~runner,selector,needs,head,recover(~S,~K,~V,~Q,~C,~R,~E,~runner,rest,kept,H.Owners{exit,tail,enter})) 118 case Position{selector,needs,SelectedEnter{}} <> rest kept H.Owners{exit,transition,head <> tail}: recovery_join(~S,~K,~V,~Q,~C,~R,~E,~runner,selector,needs,head,recover(~S,~K,~V,~Q,~C,~R,~E,~runner,rest,kept,H.Owners{exit,transition,tail})) 119 case positions kept owners: Recovery{[],positions,kept,owners} 120 def recovered(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,namespace:U32,recovery:Recovery,world:W.World,status:Status) -> BundleResult: 121 match recovery: 122 case Recovery{entries,[],[],H.Owners{[],[],[]}}: Returned{Bundle{namespace,entries},world,status} 123 case recovery: InternalOwnerRemainder{namespace,recovery,world,status} 124 def finished(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,namespace:U32,positions:List<&2,Position>,kept:List<&1,H.Entry>,result:H.HandlerResult) -> BundleResult: 125 match result: 126 case H.Completed{owners,world}: recovered(~S,~K,~V,~Q,~C,~R,~E,~runner,namespace,recover(~S,~K,~V,~Q,~C,~R,~E,~runner,positions,kept,owners),world,Completed{}) 127 case H.Stopped{owners,world,phase,error}: recovered(~S,~K,~V,~Q,~C,~R,~E,~runner,namespace,recover(~S,~K,~V,~Q,~C,~R,~E,~runner,positions,kept,owners),world,HandlerFailed{phase,error}) 128 129 def missing_selected(~Q:Data,allowed:Bool,value:Q,rest:List<&2,Q>) -> List<&2,Q>: 130 match allowed: 131 case True{}: rest 132 case False{}: value <> rest 133 def missing_join_with(~State:Type,~Q:Data,allowed:Bool,value:Q,pair:State & List<&2,Q>) -> State & List<&2,Q>: 134 (world,rest)=pair 135 (world,missing_selected(~Q,allowed,value,rest)) 136 def missing_checked_with(~State:Type,~Q:Data,~has:State -> Q -> State & Bool,pair:State & Bool,+value:Q,next:State -> State & List<&2,Q>) -> State & List<&2,Q>: 137 (world,allowed)=pair 138 missing_join_with(~State,~Q,allowed,value,next(world)) 139 def missing_with(~State:Type,~Q:Data,~has:State -> Q -> State & Bool,needs:List<&2,Q>,world:State) -> State & List<&2,Q>: 140 match needs: 141 case []: (world,[]) 142 case +value <> rest: missing_checked_with(~State,~Q,~has,has(world,value),value,w => missing_with(~State,~Q,~has,rest,w)) 143 144 def missing_join(~S:Data,~C:Type,~R:Type,~E:Data,~Q:Data,allowed:Bool,value:Q,pair:W.World & List<&2,Q>) -> W.World & List<&2,Q>: 145 (world,rest)=pair 146 (world,missing_selected(~Q,allowed,value,rest)) 147 def missing_checked(~S:Data,~C:Type,~R:Type,~E:Data,~Q:Data,~has:W.World -> Q -> W.World & Bool,pair:W.World & Bool,+value:Q,next:W.World -> W.World & List<&2,Q>) -> W.World & List<&2,Q>: 148 (world,allowed)=pair 149 missing_join(~S,~C,~R,~E,~Q,allowed,value,next(world)) 150 def missing(~S:Data,~C:Type,~R:Type,~E:Data,~Q:Data,~has:W.World -> Q -> W.World & Bool,needs:List<&2,Q>,world:W.World) -> W.World & List<&2,Q>: 151 missing_with(~W.World,~Q,~has,needs,world) 152 153 def partitioned_run(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,namespace:U32,world:W.World,captured:M.Snapshot,selected:Partition) -> BundleResult: 154 match selected: 155 case Partition{positions,kept,owners}: finished(~S,~K,~V,~Q,~C,~R,~E,~runner,namespace,positions,kept,H.run(~S,~C,~R,~E,~V,~runner,~K,~take,~put,~publish,~same,owners,world,captured)) 156 def selected_target(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,namespace:U32,entries:List<&1,Scheduled>,world:W.World,+slot:M.Slot,+current:V,pending:M.Pending) -> BundleResult: 157 match pending: 158 case M.NoPending{}: Returned{Bundle{namespace,entries},put(world,M.clear_changed(~S,~K,~V,slot)),Completed{}} 159 case M.Queued{+value,+skipSame}: partitioned_run(~S,~K,~V,~Q,~C,~R,~E,~runner,~take,~put,~publish,~same,namespace,put(world,slot),M.Scheduled{value,skipSame},partition(~S,~K,~V,~Q,~C,~R,~E,~runner,~same,entries,current,value)) 160 def selected_run(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,namespace:U32,entries:List<&1,Scheduled>,world:W.World,slot:M.Slot) -> BundleResult: 161 match slot: 162 case M.Missing{}: Returned{Bundle{namespace,entries},put(world,M.Missing{}),MachineUnavailable{}} 163 case M.Present{+current,+pending,+previous,+changed}: selected_target(~S,~K,~V,~Q,~C,~R,~E,~runner,~take,~put,~publish,~same,namespace,entries,world,M.Present{current,pending,previous,changed},current,pending) 164 def taken_run(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,namespace:U32,entries:List<&1,Scheduled>,pair:W.World & M.Slot) -> BundleResult: 165 (world,slot)=pair 166 selected_run(~S,~K,~V,~Q,~C,~R,~E,~runner,~take,~put,~publish,~same,namespace,entries,world,slot) 167 def preflighted(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,~requirement_same:Q -> Q -> Bool,~has:W.World -> Q -> W.World & Bool,~frame:W.World -> W.World,namespace:U32,entries:List<&1,Scheduled>,checked:W.World & List<&2,Q>) -> BundleResult: 168 match checked: 169 case (world,[]): taken_run(~S,~K,~V,~Q,~C,~R,~E,~runner,~take,~put,~publish,~same,namespace,entries,take(frame(world))) 170 case (world,missing): Returned{Bundle{namespace,entries},world,MissingRequirements{missing}} 171 def gathered(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,~requirement_same:Q -> Q -> Bool,~has:W.World -> Q -> W.World & Bool,~frame:W.World -> W.World,namespace:U32,world:W.World,pair:List<&1,Scheduled> & List<&2,Q>) -> BundleResult: 172 (entries,needs)=pair 173 preflighted(~S,~K,~V,~Q,~C,~R,~E,~runner,~take,~put,~publish,~same,~requirement_same,~has,~frame,namespace,entries,missing(~S,~C,~R,~E,~Q,~has,collect(~Q,~requirement_same,needs,[]),world)) 174 def namespace_checked(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,~requirement_same:Q -> Q -> Bool,~has:W.World -> Q -> W.World & Bool,~frame:W.World -> W.World,allowed:Bool,bundle:Bundle,world:W.World) -> BundleResult: 175 match allowed bundle: 176 case False{} bundle: Returned{bundle,world,ForeignBundle{}} 177 case True{} Bundle{namespace,entries}: gathered(~S,~K,~V,~Q,~C,~R,~E,~runner,~take,~put,~publish,~same,~requirement_same,~has,~frame,namespace,world,requirements(~S,~K,~V,~Q,~C,~R,~E,~runner,entries)) 178 def namespace_join(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,~requirement_same:Q -> Q -> Bool,~has:W.World -> Q -> W.World & Bool,~frame:W.World -> W.World,+namespace:U32,entries:List<&1,Scheduled>,pair:W.World & U32) -> BundleResult: 179 (world,actual)=pair 180 namespace_checked(~S,~K,~V,~Q,~C,~R,~E,~runner,~take,~put,~publish,~same,~requirement_same,~has,~frame,U32.is_eq(namespace,actual),Bundle{namespace,entries},world) 181 def run(~S:Data,~K:Data,~V:Data,~Q:Data,~C:Type,~R:Type,~E:Data,~runner:W.World -> H.Invocation -> Sys.Outcome,String,Unit>,~take:W.World -> W.World & M.Slot,~put:W.World -> M.Slot -> W.World,~publish:W.World -> M.Transition -> W.World,~same:V -> V -> Bool,~requirement_same:Q -> Q -> Bool,~has:W.World -> Q -> W.World & Bool,~frame:W.World -> W.World,bundle:Bundle,world:W.World) -> BundleResult: 182 match bundle: 183 case Bundle{namespace,entries}: namespace_join(~S,~K,~V,~Q,~C,~R,~E,~runner,~take,~put,~publish,~same,~requirement_same,~has,~frame,namespace,entries,W.namespace(~S,~C,~R,~E,world))