1 import Base 2 import ./execution_resource_policy.bend as Policy 3 import ./chunk_column.bend as Column 4 import ./owner_product.bend as Product 5 import ./lifecycle.bend as Lifecycle 6 import ./change_tick.bend as Tick 7 import ./schema_fragments.bend as Fragment 8 9 type Viewed<-P:Type,-V:Data> is Type: 10 Viewed{owner:P,view:V} 11 def read(~P:Type,~V:Data,value:Viewed
) -> Viewed
& V: 12 match value: 13 case Viewed{owner,+view}:(Viewed{owner,view},view) 14 def initialized(-P:Type,-V:Data,result:P & V) -> Viewed
:
15 match result:
16 case (owner,view):Viewed{owner,view}
17
18 type Layout is Type:
19 EmptyLayout{}
20 LayoutLeaf{P:Type,V:Data,refresh:P -> P & V,key:String,access:Policy.Policy}
21 ClassifiedLayoutLeaf{P:Type,V:Data,refresh:P -> P & V,key:String,persistence:Fragment.PersistenceKind,access:Policy.Policy}
22 LayoutPair{left:Layout,right:Layout}
23 type Route is Data:
24 AtLeaf{}
25 InLeft{rest:Route}
26 InRight{rest:Route}
27 type ColumnSlot<-S:Data,-P:Type,-key:String,-origin:Layout> is Type:
28 ColumnSlot{column:Column.Column ,transform:P -> P & Out,value:Viewed ) -> Viewed & Out:
93 match value:
94 case Viewed{owner,_}:rewritten(P,Viewed ,Out,initialize,transform(owner))
95 def rewrite(~layout:Layout,~route:Route,-Out:Type,transform:payload(layout,route) -> payload(layout,route) & Out,value:stored(layout,route)) -> stored(layout,route) & Out:
96 rewrite_view(payload(layout,route),view(layout,route),Out,initialize(layout,route),transform,value)
97 def key(layout:Layout,route:Route) -> String:
98 match layout route:
99 case LayoutLeaf{_,V,_,key,_} AtLeaf{}:key
100 case ClassifiedLayoutLeaf{_,V,_,key,_,_} AtLeaf{}:key
101 case LayoutPair{left,_} InLeft{rest}:key(left,rest)
102 case LayoutPair{_,right} InRight{rest}:key(right,rest)
103 case _ _:""
104 def valid(layout:Layout,route:Route) -> Bool:
105 match layout route:
106 case LayoutLeaf{_,V,_,_,_} AtLeaf{}:True{}
107 case ClassifiedLayoutLeaf{_,V,_,_,_,_} AtLeaf{}:True{}
108 case LayoutPair{left,_} InLeft{rest}:valid(left,rest)
109 case LayoutPair{_,right} InRight{rest}:valid(right,rest)
110 case _ _:False{}
111 def empty_column(-S:Data,-P:Type) -> Column.Column )}
117 case ClassifiedLayoutLeaf{P,V,_,key,_,_}:ColumnSlot{empty_column(S,Viewed )}
118 case LayoutPair{left,right}:Product.Owner{empty_columns(~S,left),empty_columns(~S,right)}
119 def column_rest(~S:Data,layout:Layout,route:Route) -> Type:
120 match layout route:
121 case LayoutLeaf{_,V,_,_,_} AtLeaf{}:Unit
122 case ClassifiedLayoutLeaf{_,V,_,_,_,_} AtLeaf{}:Unit
123 case LayoutPair{left,right} InLeft{rest}:columns(~S,right) & column_rest(~S,left,rest)
124 case LayoutPair{left,right} InRight{rest}:columns(~S,left) & column_rest(~S,right,rest)
125 case layout _:columns(~S,layout)
126 def column_joined(~S:Data,-P:Type,-key:String,-origin:Layout,-Outer:Type,-Inner:Type,outer:Outer,result:Inner & ColumnSlot}
29 type ResourceSlot<-S:Data,-P:Type,-key:String,-origin:Layout> is Type:
30 ResourceSlot{owner:P,stamp:Lifecycle.Stamp}
31 def unknown_stamp() -> Lifecycle.Stamp:Lifecycle.Stamp{None{},None{}}
32 def origin(layout:Layout,route:Route) -> Layout:
33 match layout route:
34 case LayoutLeaf{P,V,refresh,key,accessPolicy} AtLeaf{}:LayoutLeaf{P,V,refresh,key,accessPolicy}
35 case ClassifiedLayoutLeaf{P,V,refresh,key,kind,accessPolicy} AtLeaf{}:ClassifiedLayoutLeaf{P,V,refresh,key,kind,accessPolicy}
36 case LayoutPair{left,_} InLeft{rest}:origin(left,rest)
37 case LayoutPair{_,right} InRight{rest}:origin(right,rest)
38 case _ _:EmptyLayout{}
39 # Policy is part of the full canonical origin, independently of persistence.
40 def policy(layout:Layout,route:Route) -> Policy.Policy:
41 match layout route:
42 case LayoutLeaf{_,_,_,_,access} AtLeaf{}:access
43 case ClassifiedLayoutLeaf{_,_,_,_,_,access} AtLeaf{}:access
44 case LayoutPair{left,_} InLeft{rest}:policy(left,rest)
45 case LayoutPair{_,right} InRight{rest}:policy(right,rest)
46 case _ _:Policy.Ordinary{}
47 def columns(~S:Data,layout:Layout) -> Type:
48 match layout:
49 case EmptyLayout{}:Unit
50 case LayoutLeaf{P,V,refresh,key,accessPolicy}:ColumnSlot,key,LayoutLeaf{P,V,refresh,key,accessPolicy}>
51 case ClassifiedLayoutLeaf{P,V,refresh,key,kind,accessPolicy}:ColumnSlot,key,ClassifiedLayoutLeaf{P,V,refresh,key,kind,accessPolicy}>
52 case LayoutPair{left,right}:Product.Owner,key,LayoutLeaf{P,V,refresh,key,accessPolicy}>
57 case ClassifiedLayoutLeaf{P,V,refresh,key,kind,accessPolicy}:ResourceSlot,key,ClassifiedLayoutLeaf{P,V,refresh,key,kind,accessPolicy}>
58 case LayoutPair{left,right}:Product.Owner:
112 Column.Leaf{ANode{ANode{ANode{ALeaf{None{}},ALeaf{None{}}},ANode{ALeaf{None{}},ALeaf{None{}}}},ANode{ANode{ALeaf{None{}},ALeaf{None{}}},ANode{ALeaf{None{}},ALeaf{None{}}}}},[Lifecycle.Stamp{None{},None{}} : Lifecycle.Stamp^3n]}
113 def empty_columns(~S:Data,layout:Layout) -> columns(~S,layout):
114 match layout:
115 case EmptyLayout{}:Unit{}
116 case LayoutLeaf{P,V,_,key,_}:ColumnSlot{empty_column(S,Viewed) -> (Outer & Inner) & ColumnSlot:
127 match result:
128 case (inner,slot):((outer,inner),slot)
129 def column_take(~S:Data,layout:Layout,route:Route,owner:columns(~S,layout)) -> column_rest(~S,layout,route) & ColumnSlot:
130 match layout route owner:
131 case EmptyLayout{} _ owner:(owner,ColumnSlot{Column.empty(~S,~Viewed) -> columns(~S,layout):
142 match layout route rest:
143 case EmptyLayout{} _ rest:rest
144 case LayoutLeaf{P,V,_,key,_} AtLeaf{} _:slot
145 case ClassifiedLayoutLeaf{P,V,_,key,_,_} AtLeaf{} _:slot
146 case LayoutLeaf{P,V,_,key,_} InLeft{_} rest:rest
147 case ClassifiedLayoutLeaf{P,V,_,key,_,_} InLeft{_} rest:rest
148 case LayoutLeaf{P,V,_,key,_} InRight{_} rest:rest
149 case ClassifiedLayoutLeaf{P,V,_,key,_,_} InRight{_} rest:rest
150 case LayoutPair{left,right} AtLeaf{} rest:rest
151 case LayoutPair{left,right} InLeft{path}, (outer,inner):Product.Owner{column_put(~S,left,path,inner,slot),outer}
152 case LayoutPair{left,right} InRight{path}, (outer,inner):Product.Owner{outer,column_put(~S,right,path,inner,slot)}
153 def resource_rest_generic(-S:Data,layout:Layout,route:Route) -> Type:
154 match layout route:
155 case LayoutLeaf{_,V,_,_,_} AtLeaf{}:Unit
156 case ClassifiedLayoutLeaf{_,V,_,_,_,_} AtLeaf{}:Unit
157 case LayoutPair{left,right} InLeft{rest}:resources_generic(S,right) & resource_rest_generic(S,left,rest)
158 case LayoutPair{left,right} InRight{rest}:resources_generic(S,left) & resource_rest_generic(S,right,rest)
159 case layout _:resources_generic(S,layout)
160 def resource_rest(~S:Data,layout:Layout,route:Route) -> Type:
161 resource_rest_generic(S,layout,route)
162
163 def resource_joined_generic(-S:Data,-P:Type,-key:String,-origin:Layout,-Outer:Type,-Inner:Type,outer:Outer,result:Inner & ResourceSlot) -> (Outer & Inner) & ResourceSlot:
164 match result:
165 case (inner,slot):((outer,inner),slot)
166 def resource_joined(~S:Data,-P:Type,-key:String,-origin:Layout,-Outer:Type,-Inner:Type,outer:Outer,result:Inner & ResourceSlot) -> (Outer & Inner) & ResourceSlot:
167 resource_joined_generic(S,P,key,origin,Outer,Inner,outer,result)
168 def resource_take_generic(-S:Data,layout:Layout,route:Route,owner:resources_generic(S,layout)) -> resource_rest_generic(S,layout,route) & ResourceSlot:
169 match layout route owner:
170 case EmptyLayout{} _ owner:(owner,ResourceSlot{Viewed{Unit{},Unit{}},unknown_stamp()})
171 case LayoutLeaf{P,V,_,key,_} AtLeaf{} owner:(Unit{},owner)
172 case ClassifiedLayoutLeaf{P,V,_,key,_,_} AtLeaf{} owner:(Unit{},owner)
173 case LayoutLeaf{P,V,_,key,_} InLeft{_} owner:(owner,ResourceSlot{Viewed{Unit{},Unit{}},unknown_stamp()})
174 case ClassifiedLayoutLeaf{P,V,_,key,_,_} InLeft{_} owner:(owner,ResourceSlot{Viewed{Unit{},Unit{}},unknown_stamp()})
175 case LayoutLeaf{P,V,_,key,_} InRight{_} owner:(owner,ResourceSlot{Viewed{Unit{},Unit{}},unknown_stamp()})
176 case ClassifiedLayoutLeaf{P,V,_,key,_,_} InRight{_} owner:(owner,ResourceSlot{Viewed{Unit{},Unit{}},unknown_stamp()})
177 case LayoutPair{left,right} AtLeaf{} owner:(owner,ResourceSlot{Viewed{Unit{},Unit{}},unknown_stamp()})
178 case LayoutPair{left,right} InLeft{rest} Product.Owner{l,r}:resource_joined_generic(S,stored(left,rest),key(left,rest),origin(left,rest),resources_generic(S,right),resource_rest_generic(S,left,rest),r,resource_take_generic(S,left,rest,l))
179 case LayoutPair{left,right} InRight{rest} Product.Owner{l,r}:resource_joined_generic(S,stored(right,rest),key(right,rest),origin(right,rest),resources_generic(S,left),resource_rest_generic(S,right,rest),l,resource_take_generic(S,right,rest,r))
180 def resource_take(~S:Data,layout:Layout,route:Route,owner:resources(~S,layout)) -> resource_rest(~S,layout,route) & ResourceSlot:
181 resource_take_generic(S,layout,route,owner)
182 def resource_put_generic(-S:Data,layout:Layout,route:Route,rest:resource_rest_generic(S,layout,route),slot:ResourceSlot) -> resources_generic(S,layout):
183 match layout route rest:
184 case EmptyLayout{} _ rest:rest
185 case LayoutLeaf{P,V,_,key,_} AtLeaf{} _:slot
186 case ClassifiedLayoutLeaf{P,V,_,key,_,_} AtLeaf{} _:slot
187 case LayoutLeaf{P,V,_,key,_} InLeft{_} rest:rest
188 case ClassifiedLayoutLeaf{P,V,_,key,_,_} InLeft{_} rest:rest
189 case LayoutLeaf{P,V,_,key,_} InRight{_} rest:rest
190 case ClassifiedLayoutLeaf{P,V,_,key,_,_} InRight{_} rest:rest
191 case LayoutPair{left,right} AtLeaf{} rest:rest
192 case LayoutPair{left,right} InLeft{path}, (outer,inner):Product.Owner{resource_put_generic(S,left,path,inner,slot),outer}
193 case LayoutPair{left,right} InRight{path}, (outer,inner):Product.Owner{outer,resource_put_generic(S,right,path,inner,slot)}
194 def resource_put(~S:Data,layout:Layout,route:Route,rest:resource_rest(~S,layout,route),slot:ResourceSlot) -> resources(~S,layout):
195 resource_put_generic(S,layout,route,rest,slot)
196 def checked_owner(-A:Type,-B:Type,same:{A == B : Type},owner:A) -> B:
197 %same : _
198 owner
199 def names(layout:Layout) -> List<&2,String>:
200 match layout:
201 case EmptyLayout{}:[]
202 case LayoutLeaf{_,V,_,name,_}:[name]
203 case ClassifiedLayoutLeaf{_,V,_,name,_,_}:[name]
204 case LayoutPair{left,right}:List.append(&2,String,names(left),names(right))
205 def contains_name(values:List<&2,String>,+name:String) -> Bool:
206 match values:
207 case []:False{}
208 case current <> rest:Bool.or(String.eq(current,name),contains_name(rest,name))
209 def unique_names(values:List<&2,String>) -> Bool:
210 match values:
211 case []:True{}
212 case +name <> +rest:Bool.and(Bool.not(contains_name(rest,name)),unique_names(rest))
213 def unique(layout:Layout) -> Bool:unique_names(names(layout))
214 def grant_valid(~layout:Layout,~route:Route) -> Bool:
215 Bool.and(unique(layout),valid(layout,route))
216 def resource_write_valid(~layout:Layout,~route:Route) -> Bool:
217 Bool.and(grant_valid(~layout,~route),Policy.generic_write(policy(layout,route)))
218
219 def subtree(layout:Layout,route:Route) -> Layout:
220 match layout route:
221 case layout AtLeaf{}:layout
222 case LayoutPair{left,_} InLeft{rest}:subtree(left,rest)
223 case LayoutPair{_,right} InRight{rest}:subtree(right,rest)
224 case _ _:EmptyLayout{}
225 def subtree_rest(~S:Data,layout:Layout,route:Route) -> Type:
226 match layout route:
227 case _ AtLeaf{}:Unit
228 case LayoutPair{left,right} InLeft{rest}:columns(~S,right) & subtree_rest(~S,left,rest)
229 case LayoutPair{left,right} InRight{rest}:columns(~S,left) & subtree_rest(~S,right,rest)
230 case layout _:columns(~S,layout)
231 def subtree_joined(-Outer:Type,-Inner:Type,-Selected:Type,outer:Outer,result:Inner & Selected) -> (Outer & Inner) & Selected:
232 match result:
233 case (inner,selected):((outer,inner),selected)
234 def subtree_take(~S:Data,layout:Layout,route:Route,owner:columns(~S,layout)) -> subtree_rest(~S,layout,route) & columns(~S,subtree(layout,route)):
235 match layout route owner:
236 case layout AtLeaf{} owner:(Unit{},owner)
237 case EmptyLayout{} InLeft{_} owner:(owner,Unit{})
238 case EmptyLayout{} InRight{_} owner:(owner,Unit{})
239 case LayoutLeaf{_,V,_,_,_} InLeft{_} owner:(owner,Unit{})
240 case ClassifiedLayoutLeaf{_,V,_,_,_,_} InLeft{_} owner:(owner,Unit{})
241 case LayoutLeaf{_,V,_,_,_} InRight{_} owner:(owner,Unit{})
242 case ClassifiedLayoutLeaf{_,V,_,_,_,_} InRight{_} owner:(owner,Unit{})
243 case LayoutPair{left,right} InLeft{rest} Product.Owner{l,r}:subtree_joined(columns(~S,right),subtree_rest(~S,left,rest),columns(~S,subtree(left,rest)),r,subtree_take(~S,left,rest,l))
244 case LayoutPair{left,right} InRight{rest} Product.Owner{l,r}:subtree_joined(columns(~S,left),subtree_rest(~S,right,rest),columns(~S,subtree(right,rest)),l,subtree_take(~S,right,rest,r))
245 def subtree_put(~S:Data,layout:Layout,route:Route,rest:subtree_rest(~S,layout,route),selected:columns(~S,subtree(layout,route))) -> columns(~S,layout):
246 match layout route rest:
247 case _ AtLeaf{} _:selected
248 case EmptyLayout{} InLeft{_} rest:rest
249 case EmptyLayout{} InRight{_} rest:rest
250 case LayoutLeaf{_,V,_,_,_} InLeft{_} rest:rest
251 case ClassifiedLayoutLeaf{_,V,_,_,_,_} InLeft{_} rest:rest
252 case LayoutLeaf{_,V,_,_,_} InRight{_} rest:rest
253 case ClassifiedLayoutLeaf{_,V,_,_,_,_} InRight{_} rest:rest
254 case LayoutPair{left,right} InLeft{path}, (outer,inner):Product.Owner{subtree_put(~S,left,path,inner,selected),outer}
255 case LayoutPair{left,right} InRight{path}, (outer,inner):Product.Owner{outer,subtree_put(~S,right,path,inner,selected)}
256 def pair_input(layout:Layout) -> Type:
257 match layout:
258 case LayoutPair{LayoutLeaf{P,V,_,_,_},LayoutLeaf{_,V,_,_,_}}:P
259 case LayoutPair{LayoutLeaf{P,V,_,_,_},ClassifiedLayoutLeaf{_,V,_,_,_,_}}:P
260 case LayoutPair{ClassifiedLayoutLeaf{P,V,_,_,_,_},LayoutLeaf{_,V,_,_,_}}:P
261 case LayoutPair{ClassifiedLayoutLeaf{P,V,_,_,_,_},ClassifiedLayoutLeaf{_,V,_,_,_,_}}:P
262 case _:Unit
263 def pair_output(layout:Layout) -> Type:
264 match layout:
265 case LayoutPair{LayoutLeaf{_,V,_,_,_},LayoutLeaf{P,V,_,_,_}}:P
266 case LayoutPair{LayoutLeaf{_,V,_,_,_},ClassifiedLayoutLeaf{P,V,_,_,_,_}}:P
267 case LayoutPair{ClassifiedLayoutLeaf{_,V,_,_,_,_},LayoutLeaf{P,V,_,_,_}}:P
268 case LayoutPair{ClassifiedLayoutLeaf{_,V,_,_,_,_},ClassifiedLayoutLeaf{P,V,_,_,_,_}}:P
269 case _:Unit
270 def pair_leaves(layout:Layout) -> Bool:
271 match layout:
272 case LayoutPair{LayoutLeaf{_,V,_,_,_},LayoutLeaf{_,V,_,_,_}}:True{}
273 case LayoutPair{LayoutLeaf{_,V,_,_,_},ClassifiedLayoutLeaf{_,V,_,_,_,_}}:True{}
274 case LayoutPair{ClassifiedLayoutLeaf{_,V,_,_,_,_},LayoutLeaf{_,V,_,_,_}}:True{}
275 case LayoutPair{ClassifiedLayoutLeaf{_,V,_,_,_,_},ClassifiedLayoutLeaf{_,V,_,_,_,_}}:True{}
276 case _:False{}
277 def query_view(layout:Layout,route:Route) -> Data:view(subtree(layout,route),InLeft{AtLeaf{}})
278 def query_input(layout:Layout,route:Route) -> Type:payload(subtree(layout,route),InLeft{AtLeaf{}})
279 def query_output(layout:Layout,route:Route) -> Type:payload(subtree(layout,route),InRight{AtLeaf{}})
280 def query_read_key(layout:Layout,route:Route) -> String:key(subtree(layout,route),InLeft{AtLeaf{}})
281 def query_write_key(layout:Layout,route:Route) -> String:key(subtree(layout,route),InRight{AtLeaf{}})
282 def query_pair(~S:Data,~layout:Layout,~route:Route) -> Type:
283 Product.Owner>
284 def query_valid(~layout:Layout,~route:Route) -> Bool:
285 Bool.and(unique(layout),pair_leaves(subtree(layout,route)))
286
287 def normalize_resources(~S:Data,layout:Layout,owners:resources(~S,layout)) -> resources(~S,layout):
288 match layout owners:
289 case EmptyLayout{} owner:owner
290 case LayoutLeaf{P,V,refresh,key,accessPolicy} ResourceSlot{Viewed{owner,_},stamp}:ResourceSlot{initialized(P,V,refresh(owner)),stamp}
291 case ClassifiedLayoutLeaf{P,V,refresh,key,_,accessPolicy} ResourceSlot{Viewed{owner,_},stamp}:ResourceSlot{initialized(P,V,refresh(owner)),stamp}
292 case LayoutPair{left,right} Product.Owner{l,r}:Product.Owner{normalize_resources(~S,left,l),normalize_resources(~S,right,r)}
293
294 # Called only by a genuine ordinary World creation boundary, never by reads.
295 def initialize_resources(~S:Data,layout:Layout,+tick:Tick.Tick,owners:resources(~S,layout)) -> resources(~S,layout):
296 match layout owners:
297 case EmptyLayout{} owner:owner
298 case LayoutLeaf{P,V,refresh,key,accessPolicy} ResourceSlot{Viewed{owner,_},_}:ResourceSlot{initialized(P,V,refresh(owner)),Lifecycle.Stamp{Some{tick},Some{tick}}}
299 case ClassifiedLayoutLeaf{P,V,refresh,key,_,accessPolicy} ResourceSlot{Viewed{owner,_},_}:ResourceSlot{initialized(P,V,refresh(owner)),Lifecycle.Stamp{Some{tick},Some{tick}}}
300 case LayoutPair{left,right} Product.Owner{l,r}:Product.Owner{initialize_resources(~S,left,tick,l),initialize_resources(~S,right,tick,r)}