row_column.bend

bendvy-ecs-core@0.1.0.0 · 67 lines · 0 definitions

1import Base
2import bendvy-ecs-core@0.1.0.0/change_tick.bend as Tick
3import column.bend as Col
4import lifecycle.bend as L
5
6# The captured ID and actual column travel with the owner in one affine token.
7# No attach API accepts a different column or an external target ID.
8type Cold<-S:Data,-C:Type> is Type:
9  Cold{column:Col.Column<S,C>}
10  Wrapped{inner:Cold<S,C>}
11type Cell<-S:Data,-C:Type> is Type:
12  Taken{id:U32,owner:Maybe<&1,C>,stamp:L.Stamp,dirty:Bool,cold:Cold<S,C>}
13  Untaken{column:Col.Column<S,C>,incoming:Maybe<&1,C>,error:Unit}
14type Projected<-S:Data,-C:Type,-V:Data> is Type:
15  Projected{cell:Cell<S,C>,value:Maybe<&2,V>}
16  Deferred{cell:Cell<S,C>}
17
18def cold_column(~S:Data,~C:Type,cold:Cold<S,C>) -> Col.Column<S,C>:
19  match cold:
20    case Cold{column}: column
21    case Wrapped{inner}: cold_column(~S,~C,inner)
22def detached(~S:Data,~C:Type,+id:U32,stamp:L.Stamp,result:Col.SwapOutcome<S,C>) -> Cell<S,C>:
23  match result:
24    case Col.Accepted{column,owner}: Taken{id,owner,stamp,False{},Cold{column}}
25    case Col.Rejected{column,incoming}: Untaken{column,incoming,Unit{}}
26def detach_stamped(~S:Data,~C:Type,+id:U32,result:Col.Column<S,C> & L.Stamp) -> Cell<S,C>:
27  match result:
28    case (column,stamp): detached(~S,~C,id,stamp,Col.swap(~S,~C,column,id,None{}))
29def detach(~S:Data,~C:Type,column:Col.Column<S,C>,+id:U32) -> Cell<S,C>:
30  detach_stamped(~S,~C,id,Col.lifecycle(~S,~C,column,id))
31
32def projected_owner(~S:Data,~C:Type,~V:Data,+id:U32,stamp:L.Stamp,dirty:Bool,cold:Cold<S,C>,result:C & V) -> Projected<S,C,V>:
33  match result:
34    case (owner,value): Projected{Taken{id,Some{owner},stamp,dirty,cold},Some{value}}
35def project(~S:Data,~C:Type,~V:Data,~project:C -> C & V,cell:Cell<S,C>) -> Projected<S,C,V>:
36  match cell:
37    case Taken{id,None{},stamp,dirty,cold}: Projected{Taken{id,None{},stamp,dirty,cold},None{}}
38    case Taken{id,Some{owner},stamp,dirty,cold}: projected_owner(~S,~C,~V,id,stamp,dirty,cold,project(owner))
39    case Untaken{column,incoming,error}: Deferred{Untaken{column,incoming,error}}
40
41# Even a malformed token's attachment refusal returns its actual incoming owner.
42# Trusted row adapters must handle this outcome before exposing a World.
43def attach_marked(~S:Data,~C:Type,+id:U32,stamp:L.Stamp,dirty:Bool,result:Col.SwapOutcome<S,C>) -> Col.SwapOutcome<S,C>:
44  match dirty result:
45    case False{} result: result
46    case True{} Col.Accepted{column,previous}: Col.Accepted{Col.restore_stamp(~S,~C,column,id,stamp),previous}
47    case True{} Col.Rejected{column,incoming}: Col.Rejected{column,incoming}
48def attach(~S:Data,~C:Type,cell:Cell<S,C>) -> Col.SwapOutcome<S,C>:
49  match cell:
50    case Untaken{column,incoming,_}: Col.Rejected{column,incoming}
51    case Taken{+id,owner,stamp,dirty,cold}: attach_marked(~S,~C,id,stamp,dirty,Col.swap(~S,~C,cold_column(~S,~C,cold),id,owner))
52
53type Replacement<-S:Data,-C:Type> is Type:
54  Replaced{cell:Cell<S,C>,previous:Maybe<&1,C>,stamp:L.Stamp}
55  Refused{cell:Cell<S,C>,incoming:C}
56def replacement_present(~S:Data,~C:Type,+id:U32,+stamp:L.Stamp,cold:Cold<S,C>,incoming:C,+tick:Tick.Tick,observed:Maybe<&1,C> & Bool) -> Replacement<S,C>:
57  match observed:
58    case (previous,present): Replaced{Taken{id,Some{incoming},L.after_write(True{},present,stamp,tick),True{},cold},previous,stamp}
59def owner_present(~C:Type,owner:Maybe<&1,C>) -> Maybe<&1,C> & Bool:
60  match owner:
61    case None{}: (None{},False{})
62    case Some{owner}: (Some{owner},True{})
63# Caller validates entity/clock first and journals returned previous+stamp now.
64def replace(~S:Data,~C:Type,cell:Cell<S,C>,incoming:C,+tick:Tick.Tick) -> Replacement<S,C>:
65  match cell:
66    case Taken{+id,owner,+stamp,_,cold}: replacement_present(~S,~C,id,stamp,cold,incoming,tick,owner_present(~C,owner))
67    case Untaken{column,held,error}: Refused{Untaken{column,held,error},incoming}