owner_product.bend
bendvy-ecs-core@0.1.0.0 · 48 lines · 0 definitions
1import Base
2# Generic heterogeneous owner product, not a registry metadata adapter.
3# Library-generated paths retain the physical type through arbitrary nesting.
4type Owner<-L:Type,-R:Type> is Type:
5 Owner{left:L,right:R}
6type Path is Data:
7 Left{}
8 Right{}
9def Part(~L:Type,~R:Type,path:Path) -> Type:
10 match path:
11 case Left{}:L
12 case Right{}:R
13def Rest(~L:Type,~R:Type,path:Path) -> Type:
14 match path:
15 case Left{}:R
16 case Right{}:L
17def take(~L:Type,~R:Type,path:Path,owner:Owner<L,R>) -> Rest(~L,~R,path) & Part(~L,~R,path):
18 match path owner:
19 case Left{} Owner{left,right}:(right,left)
20 case Right{} Owner{left,right}:(left,right)
21def put(~L:Type,~R:Type,path:Path,rest:Rest(~L,~R,path),part:Part(~L,~R,path)) -> Owner<L,R>:
22 match path:
23 case Left{}:Owner{part,rest}
24 case Right{}:Owner{rest,part}
25# Canonical setup composes these fields; user systems only receive opaque caps.
26type Lens<-Whole:Type,-Part:Type,-Rest:Type> is Type:
27 Lens{take:Whole -> Rest & Part,put:Rest -> Part -> Whole}
28def lens(~L:Type,~R:Type,~path:Path) -> Lens<Owner<L,R>,Part(~L,~R,path),Rest(~L,~R,path)>:
29 Lens{owner=>take(~L,~R,path,owner),rest=>part=>put(~L,~R,path,rest,part)}
30def get_field(~W:Type,~P:Type,~R:Type,value:Lens<W,P,R>) -> W -> R & P:
31 match value:
32 case Lens{get,_}:get
33def put_field(~W:Type,~P:Type,~R:Type,value:Lens<W,P,R>) -> R -> P -> W:
34 match value:
35 case Lens{_,put}:put
36def joined(~A:Type,~B:Type,~P:Type,outer:A,observed:B & P) -> (A & B) & P:
37 match observed:
38 case (inner,part):((outer,inner),part)
39def inward(~W:Type,~M:Type,~A:Type,~P:Type,~B:Type,~inner:Lens<M,P,B>,observed:A & M) -> (A & B) & P:
40 match observed:
41 case (rest,middle):joined(~A,~B,~P,rest,get_field(~M,~P,~B,inner)(middle))
42def composed_take(~W:Type,~M:Type,~A:Type,~P:Type,~B:Type,~outer:Lens<W,M,A>,~inner:Lens<M,P,B>,owner:W) -> (A & B) & P:
43 inward(~W,~M,~A,~P,~B,~inner,get_field(~W,~M,~A,outer)(owner))
44def composed_put(~W:Type,~M:Type,~A:Type,~P:Type,~B:Type,~outer:Lens<W,M,A>,~inner:Lens<M,P,B>,rest:A & B,part:P) -> W:
45 match rest:
46 case (outerRest,innerRest):put_field(~W,~M,~A,outer)(outerRest,put_field(~M,~P,~B,inner)(innerRest,part))
47def compose(~W:Type,~M:Type,~A:Type,~P:Type,~B:Type,~outer:Lens<W,M,A>,~inner:Lens<M,P,B>) -> Lens<W,P,A & B>:
48 Lens{owner=>composed_take(~W,~M,~A,~P,~B,~outer,~inner,owner),rest=>part=>composed_put(~W,~M,~A,~P,~B,~outer,~inner,rest,part)}