relation_commands.bend
bendvy-ecs-core@0.1.0.0 · 115 lines · 0 definitions
1import Base
2import bendvy-ecs-core@0.1.0.0/trace_relation.bend as TraceRel
3import bendvy-ecs-core@0.1.0.0/deferred_command.bend as Deferred
4import bendvy-ecs-core@0.1.0.0/trace_facts.bend as TraceFacts
5import entity_key.bend as Key
6import relation_types.bend as G
7import relation_graph.bend as C
8import world.bend as W
9import transaction.bend as X
10
11# Experiment-only transport: never duplicates or inspects arbitrary component owners.
12# This initial seam implements relate/unrelate, not linked cleanup or keyed retention.
13type Store<S: Data,Components: Type> is Type:
14 Store{components: Components,graph: G.Graph<S>}
15type Notice<S: Data,Original: Data> is Data:
16 Original{event: Original}
17 RelationFailed{failure: G.Failure<S>}
18 ComponentRemoved{id:Key.Key}
19 CleanupPaused{frames: List<&2,G.CleanupFrame<S>>}
20type QueueStatus is Data:
21 Queued{}
22 MissingEntity{}
23def empty(~S: Data,~C: Type,components: C) -> Store<S,C>:
24 Store{components,G.Graph{[],[]}}
25def graph_read(~S: Data,~C: Type,store: Store<S,C>) -> Store<S,C> & G.Graph<S>:
26 match store:
27 case Store{components,+graph}: (Store{components,graph},graph)
28def graph_replace(~S: Data,~C: Type,store: Store<S,C>,replacement: G.Graph<S>) -> Store<S,C>:
29 match store:
30 case Store{components,_}: Store{components,replacement}
31def relate_result(~S: Data,~C: Type,result: G.CoreResult<S>,components: C) -> Store<S,C> & Maybe<&2,G.Error>:
32 match result:
33 case G.CoreResult{graph,error}: (Store{components,graph},error)
34def store_relate(~S: Data,~C: Type,store: Store<S,C>,descriptor: G.Descriptor<S>,source:Key.Key,target:Key.Key,sourceLive: Bool,targetLive: Bool) -> Store<S,C> & Maybe<&2,G.Error>:
35 match store:
36 case Store{components,graph}: relate_result(~S,~C,C.relate(~S,graph,descriptor,source,target,sourceLive,targetLive),components)
37def publish_relate(~S: Data,~C: Type,~R: Type,~E: Data,observed: W.World<S,Store<S,C>,R,Notice<S,E>> & Maybe<&2,G.Error>,descriptor: G.Descriptor<S>,source:Key.Key,target:Key.Key) -> W.World<S,Store<S,C>,R,Notice<S,E>>:
38 match observed:
39 case (world,None{}): world
40 case (world,Some{error}): W.event_publish(~S,~Store<S,C>,~R,~Notice<S,E>,world,[RelationFailed{G.Failure{descriptor,G.Relate{},source,target,error}}])
41def apply_live_result(~S: Data,~C: Type,~R: Type,~E: Data,checked: W.World<S,Store<S,C>,R,Notice<S,E>> & Bool,+descriptor: G.Descriptor<S>,+source:Key.Key,+target:Key.Key,sourceLive: Bool) -> W.World<S,Store<S,C>,R,Notice<S,E>> & Maybe<&2,G.Error>:
42 (world,targetLive) = checked
43 W.with_store(~S,~Store<S,C>,~R,~Notice<S,E>,~Maybe<&2,G.Error>,store => store_relate(~S,~C,store,descriptor,source,target,sourceLive,targetLive),world)
44def apply_source_result(~S: Data,~C: Type,~R: Type,~E: Data,checked: W.World<S,Store<S,C>,R,Notice<S,E>> & Bool,descriptor: G.Descriptor<S>,source:Key.Key,target:Key.Key,targetHandle: W.Handle<S>) -> W.World<S,Store<S,C>,R,Notice<S,E>> & Maybe<&2,G.Error>:
45 (world,sourceLive) = checked
46 apply_live_result(~S,~C,~R,~E,W.valid(~S,~Store<S,C>,~R,~Notice<S,E>,world,targetHandle),descriptor,source,target,sourceLive)
47def apply_relate_result(~S: Data,~C: Type,~R: Type,~E: Data,world: W.World<S,Store<S,C>,R,Notice<S,E>>,descriptor: G.Descriptor<S>,+source: W.Handle<S>,+target: W.Handle<S>) -> W.World<S,Store<S,C>,R,Notice<S,E>> & Maybe<&2,G.Error>:
48 match source target:
49 case W.Handle{+sn,+sid} W.Handle{+tn,+tid}: apply_source_result(~S,~C,~R,~E,W.valid(~S,~Store<S,C>,~R,~Notice<S,E>,world,W.Handle{sn,sid}),descriptor,sid,tid,W.Handle{tn,tid})
50def apply_relate(~S:Data,~C:Type,~R:Type,~E:Data,world:W.World<S,Store<S,C>,R,Notice<S,E>>,+descriptor:G.Descriptor<S>,+source:W.Handle<S>,+target:W.Handle<S>) -> W.World<S,Store<S,C>,R,Notice<S,E>>:
51 publish_relate(~S,~C,~R,~E,apply_relate_result(~S,~C,~R,~E,world,descriptor,source,target),descriptor,W.handle_key(~S,source),W.handle_key(~S,target))
52def applied_relate_traced(~S:Data,~C:Type,~R:Type,~E:Data,trace:TraceFacts.State,+descriptor:G.Descriptor<S>,+source:W.Handle<S>,+target:W.Handle<S>,result:W.World<S,Store<S,C>,R,Notice<S,E>> & Maybe<&2,G.Error>) -> W.World<S,Store<S,C>,R,Notice<S,E>> & TraceFacts.State:
53 match result:
54 case (world,+error):(publish_relate(~S,~C,~R,~E,(world,error),descriptor,W.handle_key(~S,source),W.handle_key(~S,target)),TraceRel.relate_applied(~S,trace,descriptor,source,target,error))
55def relate_command(~S:Data,~C:Type,~R:Type,~E:Data,enabled:Bool,+descriptor:G.Descriptor<S>,+source:W.Handle<S>,+target:W.Handle<S>) -> Deferred.Command<W.World<S,Store<S,C>,R,Notice<S,E>>>:
56 match enabled:
57 case False{}:Deferred.Raw{world=>apply_relate(~S,~C,~R,~E,world,descriptor,source,target)}
58 case True{}:Deferred.Observed{trace=>world=>applied_relate_traced(~S,~C,~R,~E,trace,descriptor,source,target,apply_relate_result(~S,~C,~R,~E,world,descriptor,source,target))}
59def relate_staged(~S:Data,~C:Type,~R:Type,~E:Data,+descriptor:G.Descriptor<S>,+source:W.Handle<S>,+target:W.Handle<S>,tx:X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>:
60 match tx:
61 case X.Tx{world,undo,commands,events,+trace}:X.Tx{world,undo,relate_command(~S,~C,~R,~E,TraceFacts.enabled(trace),descriptor,source,target) <> commands,events,TraceRel.relate(~S,trace,descriptor,TraceFacts.StageOperation{},source,target,TraceFacts.Attempted{})}
62def relate_refused(~S:Data,~C:Type,~R:Type,~E:Data,descriptor:G.Descriptor<S>,source:W.Handle<S>,target:W.Handle<S>,tx:X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>:
63 match tx:
64 case X.Tx{world,undo,commands,events,trace}:X.Tx{world,undo,commands,events,TraceRel.relate(~S,trace,descriptor,TraceFacts.StageOperation{},source,target,TraceFacts.Refused{"MissingEntity"})}
65def queue_checked(~S: Data,~C: Type,~R: Type,~E: Data,same: Bool,tx: X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>,descriptor: G.Descriptor<S>,source: W.Handle<S>,target: W.Handle<S>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>> & QueueStatus:
66 match same:
67 case False{}: (relate_refused(~S,~C,~R,~E,descriptor,source,target,tx),MissingEntity{})
68 case True{}: (relate_staged(~S,~C,~R,~E,descriptor,source,target,tx),Queued{})
69def queue_namespaced(~S: Data,~C: Type,~R: Type,~E: Data,checked: W.World<S,Store<S,C>,R,Notice<S,E>> & U32,undo: List<(W.World<S,Store<S,C>,R,Notice<S,E>> -> W.World<S,Store<S,C>,R,Notice<S,E>>)>,commands:List<Deferred.Command<W.World<S,Store<S,C>,R,Notice<S,E>>>>,events: List<&2,Notice<S,E>>,trace:TraceFacts.State,descriptor: G.Descriptor<S>,source: W.Handle<S>,target: W.Handle<S>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>> & QueueStatus:
70 match checked source target:
71 case (world,+namespace) W.Handle{+sn,+sid} W.Handle{+tn,+tid}: queue_checked(~S,~C,~R,~E,Bool.and(U32.is_eq(namespace,sn),U32.is_eq(namespace,tn)),X.Tx{world,undo,commands,events,trace},descriptor,W.Handle{sn,sid},W.Handle{tn,tid})
72def queue_relate(~S: Data,~C: Type,~R: Type,~E: Data,tx: X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>,descriptor: G.Descriptor<S>,source: W.Handle<S>,target: W.Handle<S>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>> & QueueStatus:
73 match tx:
74 case X.Tx{world,undo,commands,events,trace}: queue_namespaced(~S,~C,~R,~E,W.namespace(~S,~Store<S,C>,~R,~Notice<S,E>,world),undo,commands,events,trace,descriptor,source,target)
75def store_unrelate(~S: Data,~C: Type,store: Store<S,C>,descriptor: G.Descriptor<S>,source:Key.Key) -> Store<S,C> & Unit:
76 match store:
77 case Store{components,graph}: (Store{components,C.unrelate(~S,graph,descriptor,source)},Unit{})
78def discard_unit(~S: Data,~C: Type,~R: Type,~E: Data,result: W.World<S,Store<S,C>,R,Notice<S,E>> & Unit) -> W.World<S,Store<S,C>,R,Notice<S,E>>:
79 (world,_) = result
80 world
81def apply_unrelate(~S: Data,~C: Type,~R: Type,~E: Data,world: W.World<S,Store<S,C>,R,Notice<S,E>>,descriptor: G.Descriptor<S>,source: W.Handle<S>) -> W.World<S,Store<S,C>,R,Notice<S,E>>:
82 match source:
83 case W.Handle{_,id}: discard_unit(~S,~C,~R,~E,W.with_store(~S,~Store<S,C>,~R,~Notice<S,E>,~Unit,store => store_unrelate(~S,~C,store,descriptor,id),world))
84def unrelate_command(~S:Data,~C:Type,~R:Type,~E:Data,enabled:Bool,+descriptor:G.Descriptor<S>,+source:W.Handle<S>) -> Deferred.Command<W.World<S,Store<S,C>,R,Notice<S,E>>>:
85 match enabled:
86 case False{}:Deferred.Raw{world=>apply_unrelate(~S,~C,~R,~E,world,descriptor,source)}
87 case True{}:Deferred.Observed{trace=>world=>(apply_unrelate(~S,~C,~R,~E,world,descriptor,source),TraceRel.unrelate(~S,trace,descriptor,TraceFacts.ApplyOperation{},source,TraceFacts.Applied{}))}
88def unrelate_staged(~S:Data,~C:Type,~R:Type,~E:Data,+descriptor:G.Descriptor<S>,+source:W.Handle<S>,tx:X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>:
89 match tx:
90 case X.Tx{world,undo,commands,events,+trace}:X.Tx{world,undo,unrelate_command(~S,~C,~R,~E,TraceFacts.enabled(trace),descriptor,source) <> commands,events,TraceRel.unrelate(~S,trace,descriptor,TraceFacts.StageOperation{},source,TraceFacts.Attempted{})}
91def unrelate_refused(~S:Data,~C:Type,~R:Type,~E:Data,descriptor:G.Descriptor<S>,source:W.Handle<S>,tx:X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>:
92 match tx:
93 case X.Tx{world,undo,commands,events,trace}:X.Tx{world,undo,commands,events,TraceRel.unrelate(~S,trace,descriptor,TraceFacts.StageOperation{},source,TraceFacts.Refused{"MissingEntity"})}
94def queue_clear_checked(~S: Data,~C: Type,~R: Type,~E: Data,same: Bool,tx: X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>,descriptor: G.Descriptor<S>,source: W.Handle<S>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>> & QueueStatus:
95 match same:
96 case False{}: (unrelate_refused(~S,~C,~R,~E,descriptor,source,tx),MissingEntity{})
97 case True{}: (unrelate_staged(~S,~C,~R,~E,descriptor,source,tx),Queued{})
98def queue_clear_namespaced(~S: Data,~C: Type,~R: Type,~E: Data,checked: W.World<S,Store<S,C>,R,Notice<S,E>> & U32,undo: List<(W.World<S,Store<S,C>,R,Notice<S,E>> -> W.World<S,Store<S,C>,R,Notice<S,E>>)>,commands:List<Deferred.Command<W.World<S,Store<S,C>,R,Notice<S,E>>>>,events: List<&2,Notice<S,E>>,trace:TraceFacts.State,descriptor: G.Descriptor<S>,source: W.Handle<S>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>> & QueueStatus:
99 match checked source:
100 case (world,namespace) W.Handle{+sn,+sid}: queue_clear_checked(~S,~C,~R,~E,U32.is_eq(namespace,sn),X.Tx{world,undo,commands,events,trace},descriptor,W.Handle{sn,sid})
101def queue_unrelate(~S: Data,~C: Type,~R: Type,~E: Data,tx: X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>>,descriptor: G.Descriptor<S>,source: W.Handle<S>) -> X.Tx<W.World<S,Store<S,C>,R,Notice<S,E>>,Notice<S,E>> & QueueStatus:
102 match tx:
103 case X.Tx{world,undo,commands,events,trace}: queue_clear_namespaced(~S,~C,~R,~E,W.namespace(~S,~Store<S,C>,~R,~Notice<S,E>,world),undo,commands,events,trace,descriptor,source)
104def store_inverse(~S: Data,~C: Type,store: Store<S,C>,descriptor: G.Descriptor<S>,target:Key.Key) -> Store<S,C> & List<&2,Key.Key>:
105 match store:
106 case Store{components,+graph}: (Store{components,graph},C.inverse(~S,graph,descriptor,target))
107def inverse_present(~S: Data,~C: Type,~R: Type,~E: Data,result: W.World<S,Store<S,C>,R,Notice<S,E>> & List<&2,Key.Key>) -> W.World<S,Store<S,C>,R,Notice<S,E>> & Maybe<&2,List<&2,Key.Key>>:
108 (world,sources) = result
109 (world,Some{sources})
110def inverse_checked(~S: Data,~C: Type,~R: Type,~E: Data,checked: W.World<S,Store<S,C>,R,Notice<S,E>> & Bool,descriptor: G.Descriptor<S>,target: W.Handle<S>) -> W.World<S,Store<S,C>,R,Notice<S,E>> & Maybe<&2,List<&2,Key.Key>>:
111 match checked target:
112 case (world,False{}) _: (world,None{})
113 case (world,True{}) W.Handle{_,id}: inverse_present(~S,~C,~R,~E,W.with_store(~S,~Store<S,C>,~R,~Notice<S,E>,~List<&2,Key.Key>,store => store_inverse(~S,~C,store,descriptor,id),world))
114def inverse(~S: Data,~C: Type,~R: Type,~E: Data,world: W.World<S,Store<S,C>,R,Notice<S,E>>,descriptor: G.Descriptor<S>,+target: W.Handle<S>) -> W.World<S,Store<S,C>,R,Notice<S,E>> & Maybe<&2,List<&2,Key.Key>>:
115 inverse_checked(~S,~C,~R,~E,W.valid(~S,~Store<S,C>,~R,~Notice<S,E>,world,target),descriptor,target)