git.iko.soy/rowtype-yoga/purescript-yoga-om-layer
on main help sign in
.psc-ide-port+1−1
since splitsnapshots
unifiedsplit
@@ -1 +1 @@
1−15887
1+15500
.spec-results+96−33
@@ -1,232 +1,295 @@
1[
1[
2+ [
3+ "Alt falls back to second layer on failure",
4+ {
5+ "timestamp": "1775221839085.0",
6+ "success": true
7+ }
8+ ],
2 [
9 [
3 "Automatic Memoization builds the same layer only once when used in two branches",
10 "Automatic Memoization builds the same layer only once when used in two branches",
4 {
11 {
5− "timestamp": "1771091609999.0",
12+ "timestamp": "1775221839085.0",
6 "success": true
13 "success": true
7 }
14 }
8 ],
15 ],
9 [
16 [
10 "Automatic Memoization memoized bracketLayer acquires and releases only once",
17 "Automatic Memoization memoized bracketLayer acquires and releases only once",
11 {
18 {
12− "timestamp": "1771091609999.0",
19+ "timestamp": "1775221839085.0",
13 "success": true
20 "success": true
14 }
21 }
15 ],
22 ],
16 [
23 [
17 "Automatic Memoization memoized scoped layer runs finalizer only once",
24 "Automatic Memoization memoized scoped layer runs finalizer only once",
18 {
25 {
19− "timestamp": "1771091609999.0",
26+ "timestamp": "1775221839085.0",
20 "success": true
27 "success": true
21 }
28 }
22 ],
29 ],
23 [
30 [
24 "Composition proves Row.Nub deduplicates shared requirements at type level",
31 "Composition proves Row.Nub deduplicates shared requirements at type level",
25 {
32 {
26− "timestamp": "1771091609999.0",
33+ "timestamp": "1775221839085.0",
27 "success": true
34 "success": true
28 }
35 }
29 ],
36 ],
30 [
37 [
31 "Composition proves deduplication at runtime via shared Ref",
38 "Composition proves deduplication at runtime via shared Ref",
32 {
39 {
33− "timestamp": "1771091609999.0",
40+ "timestamp": "1775221839085.0",
34 "success": true
41 "success": true
35 }
42 }
36 ],
43 ],
37 [
44 [
38 "Error Channel composes layers with different error types",
45 "Error Channel composes layers with different error types",
39 {
46 {
40− "timestamp": "1771091609999.0",
47+ "timestamp": "1775221839085.0",
41 "success": true
48 "success": true
42 }
49 }
43 ],
50 ],
44 [
51 [
45 "Error Channel propagates errors from the first layer",
52 "Error Channel propagates errors from the first layer",
46 {
53 {
47− "timestamp": "1771091609999.0",
54+ "timestamp": "1775221839085.0",
48 "success": true
55 "success": true
49 }
56 }
50 ],
57 ],
51 [
58 [
52 "Error Channel propagates errors from the second layer",
59 "Error Channel propagates errors from the second layer",
53 {
60 {
54− "timestamp": "1771091609999.0",
61+ "timestamp": "1775221839085.0",
55 "success": true
62 "success": true
56 }
63 }
57 ],
64 ],
58 [
65 [
59 "Error Channel propagates errors through vertical composition",
66 "Error Channel propagates errors through vertical composition",
60 {
67 {
61− "timestamp": "1771091609999.0",
68+ "timestamp": "1775221839085.0",
62 "success": true
69 "success": true
63 }
70 }
64 ],
71 ],
65 [
72 [
66 "Error Channel scoped layer error triggers finalizers",
73 "Error Channel scoped layer error triggers finalizers",
67 {
74 {
68− "timestamp": "1771091609999.0",
75+ "timestamp": "1775221839085.0",
69 "success": true
76 "success": true
70 }
77 }
71 ],
78 ],
72 [
79 [
73 "Error Channel scoped layers with errors run via runScopedWith",
80 "Error Channel scoped layers with errors run via runScopedWith",
74 {
81 {
75− "timestamp": "1771091609999.0",
82+ "timestamp": "1775221839085.0",
76 "success": true
83 "success": true
77 }
84 }
78 ],
85 ],
79 [
86 [
80 "Functor / Apply / Bind applies two layers",
87 "Functor / Apply / Bind applies two layers",
81 {
88 {
82− "timestamp": "1771091609999.0",
89+ "timestamp": "1775221839085.0",
83 "success": true
90 "success": true
84 }
91 }
85 ],
92 ],
86 [
93 [
87 "Functor / Apply / Bind binds layers sequentially",
94 "Functor / Apply / Bind binds layers sequentially",
88 {
95 {
89− "timestamp": "1771091609999.0",
96+ "timestamp": "1775221839085.0",
90 "success": true
97 "success": true
91 }
98 }
92 ],
99 ],
93 [
100 [
94 "Functor / Apply / Bind maps over layer output",
101 "Functor / Apply / Bind maps over layer output",
95 {
102 {
96− "timestamp": "1771091609999.0",
103+ "timestamp": "1775221839085.0",
104+ "success": true
105+ }
106+ ],
107+ [
108+ "Functor / Apply / Bind runs layers against a superset context",
109+ {
110+ "timestamp": "1775221839085.0",
97 "success": true
111 "success": true
98 }
112 }
99 ],
113 ],
100 [
114 [
101 "Layer recovering does not retry on non-matching errors",
115 "Layer recovering does not retry on non-matching errors",
102 {
116 {
103− "timestamp": "1771091609999.0",
117+ "timestamp": "1775221839085.0",
104 "success": true
118 "success": true
105 }
119 }
106 ],
120 ],
107 [
121 [
108 "Layer recovering only retries the specified error in a multi-error layer",
122 "Layer recovering only retries the specified error in a multi-error layer",
109 {
123 {
110− "timestamp": "1771091609999.0",
124+ "timestamp": "1775221839085.0",
111 "success": true
125 "success": true
112 }
126 }
113 ],
127 ],
114 [
128 [
115 "Layer recovering preserves the error when retries are exhausted",
129 "Layer recovering preserves the error when retries are exhausted",
116 {
130 {
117− "timestamp": "1771091609999.0",
131+ "timestamp": "1775221839085.0",
118 "success": true
132 "success": true
119 }
133 }
120 ],
134 ],
121 [
135 [
122 "Layer recovering retries layer construction on matching errors",
136 "Layer recovering retries layer construction on matching errors",
123 {
137 {
124− "timestamp": "1771091609999.0",
138+ "timestamp": "1775221839085.0",
125 "success": true
139 "success": true
126 }
140 }
127 ],
141 ],
128 [
142 [
129 "Layer recovering uses RetryStatus in the check",
143 "Layer recovering uses RetryStatus in the check",
130 {
144 {
131− "timestamp": "1771091609999.0",
145+ "timestamp": "1775221839085.0",
132 "success": true
146 "success": true
133 }
147 }
134 ],
148 ],
135 [
149 [
136 "Layer repeating repeats layer construction while condition holds",
150 "Layer repeating repeats layer construction while condition holds",
137 {
151 {
138− "timestamp": "1771091609999.0",
152+ "timestamp": "1775221839085.0",
139 "success": true
153 "success": true
140 }
154 }
141 ],
155 ],
142 [
156 [
143 "Layer repeating stops when policy exhausted",
157 "Layer repeating stops when policy exhausted",
144 {
158 {
145− "timestamp": "1771091609999.0",
159+ "timestamp": "1775221839085.0",
160+ "success": true
161+ }
162+ ],
163+ [
164+ "MonadAsk / MonadReader ask reads the context directly",
165+ {
166+ "timestamp": "1775221839085.0",
167+ "success": true
168+ }
169+ ],
170+ [
171+ "MonadAsk / MonadReader local modifies context for a layer",
172+ {
173+ "timestamp": "1775221839085.0",
174+ "success": true
175+ }
176+ ],
177+ [
178+ "MonadEffect / MonadAff liftEffect works directly in layer bind",
179+ {
180+ "timestamp": "1775221839085.0",
181+ "success": true
182+ }
183+ ],
184+ [
185+ "MonadThrow / MonadError throwError raises into the error channel",
186+ {
187+ "timestamp": "1775221839085.0",
146 "success": true
188 "success": true
147 }
189 }
148 ],
190 ],
149 [
191 [
150 "Parallel runs layers in parallel via parApply",
192 "Parallel runs layers in parallel via parApply",
151 {
193 {
152− "timestamp": "1771091609999.0",
194+ "timestamp": "1775221839085.0",
153 "success": true
195 "success": true
154 }
196 }
155 ],
197 ],
156 [
198 [
157 "Scoped Layers combines scoped and non-scoped layers",
199 "Scoped Layers combines scoped and non-scoped layers",
158 {
200 {
159− "timestamp": "1771091609999.0",
201+ "timestamp": "1775221839085.0",
160 "success": true
202 "success": true
161 }
203 }
162 ],
204 ],
163 [
205 [
164 "Scoped Layers fresh layer builds twice even when same base layer",
206 "Scoped Layers fresh layer builds twice even when same base layer",
165 {
207 {
166− "timestamp": "1771091609999.0",
208+ "timestamp": "1775221839085.0",
167 "success": true
209 "success": true
168 }
210 }
169 ],
211 ],
170 [
212 [
171 "Scoped Layers runs finalizers in reverse order on success",
213 "Scoped Layers runs finalizers in reverse order on success",
172 {
214 {
173− "timestamp": "1771091609999.0",
215+ "timestamp": "1775221839085.0",
174 "success": true
216 "success": true
175 }
217 }
176 ],
218 ],
177 [
219 [
178 "Scoped Layers runs finalizers when the callback throws",
220 "Scoped Layers runs finalizers when the callback throws",
179 {
221 {
180− "timestamp": "1771091609999.0",
222+ "timestamp": "1775221839085.0",
181 "success": true
223 "success": true
182 }
224 }
183 ],
225 ],
184 [
226 [
185 "Scoped Layers threads finalizers through vertical composition (provide)",
227 "Scoped Layers threads finalizers through vertical composition (provide)",
186 {
228 {
187− "timestamp": "1771091609999.0",
229+ "timestamp": "1775221839085.0",
188 "success": true
230 "success": true
189 }
231 }
190 ],
232 ],
191 [
233 [
192 "Scoped Layers works with bracketLayer",
234 "Scoped Layers works with bracketLayer",
193 {
235 {
194− "timestamp": "1771091609999.0",
236+ "timestamp": "1775221839085.0",
237+ "success": true
238+ }
239+ ],
240+ [
241+ "Semigroup appends layer outputs",
242+ {
243+ "timestamp": "1775221839085.0",
244+ "success": true
245+ }
246+ ],
247+ [
248+ "combinePure combines a plain record with a layer without needing VTA",
249+ {
250+ "timestamp": "1775214062703.0",
195 "success": true
251 "success": true
196 }
252 }
197 ],
253 ],
198 [
254 [
199 "wireLayers memoizes shared dependencies within wireLayers",
255 "wireLayers memoizes shared dependencies within wireLayers",
200 {
256 {
201− "timestamp": "1771091609999.0",
257+ "timestamp": "1775221839085.0",
202 "success": true
258 "success": true
203 }
259 }
204 ],
260 ],
205 [
261 [
206 "wireLayers wireLayersDebug emits dependency graph as compiler warning",
262 "wireLayers wireLayersDebug emits dependency graph as compiler warning",
207 {
263 {
208− "timestamp": "1771091609999.0",
264+ "timestamp": "1775221839085.0",
209 "success": true
265 "success": true
210 }
266 }
211 ],
267 ],
212 [
268 [
213 "wireLayers wires a diamond dependency",
269 "wireLayers wires a diamond dependency",
214 {
270 {
215− "timestamp": "1771091609999.0",
271+ "timestamp": "1775221839085.0",
216 "success": true
272 "success": true
217 }
273 }
218 ],
274 ],
219 [
275 [
220 "wireLayers wires a simple linear chain (A -> B -> C)",
276 "wireLayers wires a simple linear chain (A -> B -> C)",
221 {
277 {
222− "timestamp": "1771091609999.0",
278+ "timestamp": "1775221839085.0",
223 "success": true
279 "success": true
224 }
280 }
225 ],
281 ],
226 [
282 [
227 "wireLayers wires independent layers with no inter-dependencies",
283 "wireLayers wires independent layers with no inter-dependencies",
228 {
284 {
229− "timestamp": "1771091609999.0",
285+ "timestamp": "1775221839085.0",
286+ "success": true
287+ }
288+ ],
289+ [
290+ "withValue combines a plain record with a layer without needing VTA",
291+ {
292+ "timestamp": "1775221839085.0",
230 "success": true
293 "success": true
231 }
294 }
232 ]
295 ]
README.md+1
@@ -240,3 +240,4 @@ combineRequirements (OmLayer build1) (OmLayer build2) = makeLayer do
240
240
241**Status**: Proof of concept complete ✓ with working runtime implementation
241**Status**: Proof of concept complete ✓ with working runtime implementation
242**Next**: Implement full instance-resolution based layer system with provideLayers API
242**Next**: Implement full instance-resolution based layer system with provideLayers API
243+
compile-fail-tests/RunLayerOpenRowMissingDep.pursnew file+19
@@ -0,0 +1,19 @@
1+-- EXPECT: Prim.RowList.RowToList
2+module Yoga.Om.Layer.CompileFailTest where
3+
4+import Prelude
5+
6+import Yoga.Om as Om
7+import Yoga.Om.Layer (OmLayer, makeLayer, runLayer)
8+
9+type Fastify = String
10+type Connection = String
11+
12+apiLayer :: forall r. OmLayer (fastify :: Fastify, sqlite :: Connection | r) () {}
13+apiLayer = makeLayer do
14+ { fastify, sqlite } <- Om.ask
15+ let _ = fastify <> sqlite
16+ pure {}
17+
18+bad :: Om.Om { fastify :: Fastify } () {}
19+bad = runLayer { fastify: "server" } apiLayer
compile-fail-tests/WireLayersMissingDep.purs+4−4
@@ -1,4 +1,4 @@
1−-- EXPECT: TypesDoNotUnify
1+-- EXPECT: Dependency not provided!
2module Yoga.Om.Layer.CompileFailTest where
2module Yoga.Om.Layer.CompileFailTest where
3
3
4import Prelude
4import Prelude
@@ -8,9 +8,9 @@ import Yoga.Om.Layer (OmLayer, Scope, makeLayer, wireLayers)
8a_base :: OmLayer (scope :: Scope) () { base :: String }
8a_base :: OmLayer (scope :: Scope) () { base :: String }
9a_base = makeLayer (pure { base: "ok" })
9a_base = makeLayer (pure { base: "ok" })
10
10
11−b_upper :: OmLayer (scope :: Scope, missing :: Int) () { upper :: String }
11+b_upper :: OmLayer (scope :: Scope, port :: Int) () { upper :: String }
12b_upper = makeLayer do
12b_upper = makeLayer do
13− { missing } <- Om.ask
13+ { port } <- Om.ask
14− pure { upper: show missing }
14+ pure { upper: show port }
15
15
16bad = wireLayers { a_base, b_upper }
16bad = wireLayers { a_base, b_upper }
compile-fail-tests/WireLayersWrongOrder.pursdeleted−17
@@ -1,17 +0,0 @@
1−-- EXPECT: TypesDoNotUnify
2−module Yoga.Om.Layer.CompileFailTest where
3−
4−import Prelude
5−import Yoga.Om as Om
6−import Yoga.Om.Layer (OmLayer, Scope, makeLayer, wireLayers)
7−
8−-- a_base needs upper, but b_upper provides it — wrong alphabetical order
9−a_needs_upper :: OmLayer (scope :: Scope, upper :: String) () { final :: String }
10−a_needs_upper = makeLayer do
11− { upper } <- Om.ask
12− pure { final: upper <> "!" }
13−
14−b_upper :: OmLayer (scope :: Scope) () { upper :: String }
15−b_upper = makeLayer (pure { upper: "hello" })
16−
17−bad = wireLayers { a_needs_upper, b_upper }
spago.lock+27−600
@@ -6,7 +6,7 @@
6 "core": {
6 "core": {
7 "dependencies": [
7 "dependencies": [
8 {
8 {
9− "aff": ">=7.1.0 <8.0.0"
9+ "aff": ">=8.0.0 <9.0.0"
10 },
10 },
11 {
11 {
12 "aff-retry": ">=2.0.0 <3.0.0"
12 "aff-retry": ">=2.0.0 <3.0.0"
@@ -14,6 +14,9 @@
14 {
14 {
15 "arrays": ">=7.3.0 <8.0.0"
15 "arrays": ">=7.3.0 <8.0.0"
16 },
16 },
17+ {
18+ "control": ">=6.0.0 <7.0.0"
19+ },
17 {
20 {
18 "effect": ">=4.0.0 <5.0.0"
21 "effect": ">=4.0.0 <5.0.0"
19 },
22 },
@@ -32,7 +35,9 @@
32 {
35 {
33 "ordered-collections": ">=3.2.0 <4.0.0"
36 "ordered-collections": ">=3.2.0 <4.0.0"
34 },
37 },
38+ {
35− "parallel",
39+ "parallel": ">=7.0.0 <8.0.0"
40+ },
36 {
41 {
37 "prelude": ">=6.0.2 <7.0.0"
42 "prelude": ">=6.0.2 <7.0.0"
38 },
43 },
@@ -42,11 +47,21 @@
42 {
47 {
43 "record-studio": ">=1.0.4 <2.0.0"
48 "record-studio": ">=1.0.4 <2.0.0"
44 },
49 },
50+ {
45− "refs",
51+ "refs": ">=6.0.0 <7.0.0"
52+ },
53+ {
54+ "tailrec": ">=6.1.0 <7.0.0"
55+ },
56+ {
46− "transformers",
57+ "transformers": ">=6.1.0 <7.0.0"
58+ },
47 {
59 {
48 "type-equality": ">=4.0.1 <5.0.0"
60 "type-equality": ">=4.0.1 <5.0.0"
49 },
61 },
62+ {
63+ "typelevel-prelude": ">=7.0.0 <8.0.0"
64+ },
50 {
65 {
51 "unsafe-coerce": ">=6.0.0 <7.0.0"
66 "unsafe-coerce": ">=6.0.0 <7.0.0"
52 },
67 },
@@ -54,7 +69,7 @@
54 "variant": ">=8.0.0 <9.0.0"
69 "variant": ">=8.0.0 <9.0.0"
55 },
70 },
56 {
71 {
57− "yoga-om": ">=1.4.0 <2.0.0"
72+ "yoga-om": ">=2.0.0 <3.0.0"
58 }
73 }
59 ]
74 ]
60 },
75 },
@@ -79,597 +94,6 @@
79 }
94 }
80 }
95 }
81 },
96 },
82− "package_set": {
83− "address": {
84− "registry": "73.0.0"
85− },
86− "compiler": ">=0.15.15 <0.16.0",
87− "content": {
88− "abc-parser": "2.1.0",
89− "ace": "9.1.0",
90− "address-rfc2821": "0.1.1",
91− "aff": "8.0.0",
92− "aff-bus": "6.0.0",
93− "aff-coroutines": "9.0.0",
94− "aff-promise": "4.0.0",
95− "aff-retry": "2.0.0",
96− "affjax": "13.0.0",
97− "affjax-node": "1.0.0",
98− "affjax-web": "1.0.0",
99− "ansi": "7.0.0",
100− "apexcharts": "0.5.0",
101− "applicative-phases": "1.0.0",
102− "argonaut": "9.0.0",
103− "argonaut-aeson-generic": "0.4.1",
104− "argonaut-codecs": "9.1.0",
105− "argonaut-core": "7.0.0",
106− "argonaut-generic": "8.0.0",
107− "argonaut-traversals": "10.0.0",
108− "argparse-basic": "2.0.0",
109− "array-builder": "0.1.2",
110− "array-search": "0.6.0",
111− "arraybuffer": "13.2.0",
112− "arraybuffer-builder": "3.1.0",
113− "arraybuffer-types": "3.0.2",
114− "arrays": "7.3.0",
115− "arrays-extra": "0.6.1",
116− "arrays-zipper": "2.0.1",
117− "ask": "1.0.0",
118− "assert": "6.0.0",
119− "assert-multiple": "0.4.0",
120− "avar": "5.0.1",
121− "axon": "0.0.3",
122− "b64": "0.0.8",
123− "barbies": "1.0.1",
124− "barlow-lens": "0.9.0",
125− "benchlib": "0.0.4",
126− "bifunctors": "6.1.0",
127− "bigints": "7.0.1",
128− "blessed": "1.0.0",
129− "bolson": "0.3.9",
130− "bookhound": "0.1.7",
131− "bower-json": "3.0.0",
132− "call-by-name": "4.0.1",
133− "canvas": "6.0.0",
134− "canvas-action": "9.0.0",
135− "cartesian": "1.0.6",
136− "catenable-lists": "7.0.0",
137− "cbor-stream": "1.3.0",
138− "chameleon": "1.0.0",
139− "chameleon-halogen": "1.0.3",
140− "chameleon-react-basic": "1.1.0",
141− "chameleon-styled": "2.5.0",
142− "chameleon-transformers": "1.0.0",
143− "channel": "1.0.0",
144− "checked-exceptions": "3.1.1",
145− "choku": "1.0.2",
146− "classless": "0.1.1",
147− "classless-arbitrary": "0.1.1",
148− "classless-decode-json": "0.1.1",
149− "classless-encode-json": "0.1.3",
150− "classnames": "2.0.0",
151− "codec": "6.1.0",
152− "codec-argonaut": "10.0.0",
153− "codec-json": "2.0.0",
154− "colors": "7.0.1",
155− "concur-core": "0.5.0",
156− "concur-react": "0.5.0",
157− "concurrent-queues": "3.0.0",
158− "console": "6.1.0",
159− "const": "6.0.0",
160− "contravariant": "6.0.0",
161− "control": "6.0.0",
162− "convertable-options": "1.0.0",
163− "coroutines": "7.0.0",
164− "css": "6.0.0",
165− "css-class-name-extractor": "0.0.4",
166− "css-frameworks": "1.1.0",
167− "csv-stream": "2.3.0",
168− "data-mvc": "0.0.2",
169− "datetime": "6.1.0",
170− "datetime-parsing": "0.2.0",
171− "debounce": "0.1.0",
172− "debug": "6.0.2",
173− "decimals": "7.1.0",
174− "default-values": "1.0.1",
175− "deku": "0.9.23",
176− "deno": "0.0.5",
177− "dissect": "1.0.0",
178− "distributive": "6.0.0",
179− "dodo-printer": "2.2.3",
180− "dom-filereader": "7.0.0",
181− "dom-indexed": "13.0.0",
182− "dom-simple": "0.4.0",
183− "dotenv": "4.0.3",
184− "droplet": "0.6.0",
185− "dts": "1.0.0",
186− "dual-numbers": "1.0.3",
187− "dynamic-buffer": "3.0.1",
188− "echarts-simple": "0.0.1",
189− "effect": "4.0.0",
190− "either": "6.1.0",
191− "elmish": "0.13.0",
192− "elmish-enzyme": "0.1.1",
193− "elmish-hooks": "0.11.0",
194− "elmish-html": "0.10.0",
195− "elmish-testing-library": "0.3.2",
196− "elmish-time-machine": "0.4.2",
197− "email-validate": "7.0.0",
198− "encoding": "0.0.9",
199− "enums": "6.0.1",
200− "env-names": "0.4.0",
201− "environment": "2.0.0",
202− "error": "2.0.0",
203− "eta-conversion": "0.3.2",
204− "exceptions": "6.1.0",
205− "exists": "6.0.0",
206− "exitcodes": "4.0.0",
207− "expect-inferred": "3.0.0",
208− "express": "0.9.1",
209− "ezfetch": "1.1.1",
210− "fahrtwind": "2.0.0",
211− "fakerjs": "0.0.1",
212− "fallback": "0.1.0",
213− "fast-vect": "1.2.0",
214− "fetch": "4.1.0",
215− "fetch-argonaut": "1.0.1",
216− "fetch-core": "5.1.0",
217− "fetch-yoga-json": "1.1.0",
218− "ffi-simple": "0.5.1",
219− "fft": "0.3.0",
220− "fft-js": "0.1.0",
221− "filterable": "5.0.0",
222− "fix-functor": "0.1.0",
223− "fixed-points": "7.0.0",
224− "fixed-precision": "5.0.0",
225− "flame": "1.6.0",
226− "float32": "2.0.0",
227− "fmt": "0.2.1",
228− "foldable-traversable": "6.0.0",
229− "foldable-traversable-extra": "0.0.6",
230− "foreign": "7.0.0",
231− "foreign-object": "4.1.0",
232− "foreign-readwrite": "3.4.0",
233− "forgetmenot": "0.1.0",
234− "fork": "6.0.0",
235− "form-urlencoded": "7.0.0",
236− "formatters": "7.0.0",
237− "framer-motion": "2.0.1",
238− "free": "7.1.0",
239− "freeap": "7.0.0",
240− "freer-free": "0.0.1",
241− "freet": "7.0.0",
242− "functions": "6.0.0",
243− "functor1": "3.0.0",
244− "functors": "5.0.0",
245− "fuzzy": "0.4.0",
246− "gen": "4.0.0",
247− "generate-values": "1.0.1",
248− "generic-router": "0.0.1",
249− "geojson": "0.0.5",
250− "geometria": "2.2.0",
251− "gesso": "1.0.1",
252− "gojs": "0.1.1",
253− "golem-fetch": "0.1.0",
254− "grain": "3.0.0",
255− "grain-router": "3.0.0",
256− "grain-virtualized": "3.0.0",
257− "graphql-client": "10.1.1",
258− "graphs": "8.1.0",
259− "group": "4.1.1",
260− "halogen": "7.0.0",
261− "halogen-bootstrap5": "5.3.2",
262− "halogen-canvas": "1.0.0",
263− "halogen-css": "10.0.0",
264− "halogen-declarative-canvas": "0.0.8",
265− "halogen-echarts-simple": "0.0.4",
266− "halogen-formless": "4.0.3",
267− "halogen-helix": "1.1.0",
268− "halogen-hooks": "0.6.3",
269− "halogen-hooks-extra": "0.9.0",
270− "halogen-infinite-scroll": "1.1.0",
271− "halogen-store": "0.5.4",
272− "halogen-storybook": "2.0.0",
273− "halogen-subscriptions": "2.0.0",
274− "halogen-svg-elems": "8.0.0",
275− "halogen-typewriter": "1.0.4",
276− "halogen-use-trigger-hooks": "1.0.0",
277− "halogen-vdom": "8.0.0",
278− "halogen-vdom-string-renderer": "0.5.0",
279− "halogen-xterm": "2.0.0",
280− "heckin": "2.0.1",
281− "heterogeneous": "0.7.0",
282− "homogeneous": "0.4.0",
283− "http-methods": "6.0.0",
284− "httpurple": "4.0.0",
285− "huffman": "0.4.0",
286− "humdrum": "0.0.1",
287− "hylograph-canvas": "0.1.0",
288− "hylograph-d3-kernel": "0.1.0",
289− "hylograph-graph": "0.1.0",
290− "hylograph-layout": "0.1.0",
291− "hylograph-music": "0.1.0",
292− "hylograph-optics": "0.1.0",
293− "hylograph-selection": "0.1.1",
294− "hylograph-simulation": "0.1.0",
295− "hylograph-simulation-core": "0.1.0",
296− "hylograph-simulation-halogen": "0.1.0",
297− "hylograph-transitions": "0.1.0",
298− "hylograph-wasm-kernel": "0.1.0",
299− "hyrule": "2.3.8",
300− "identity": "6.0.0",
301− "identy": "4.0.1",
302− "indexed-db": "1.0.0",
303− "indexed-monad": "3.0.0",
304− "ink": "0.0.1",
305− "int64": "3.0.0",
306− "integers": "6.0.0",
307− "interpolate": "5.0.2",
308− "intersection-observer": "1.0.1",
309− "invariant": "6.0.0",
310− "jarilo": "1.0.1",
311− "jelly": "0.10.0",
312− "jelly-router": "0.3.0",
313− "jelly-signal": "0.4.0",
314− "jest": "1.0.0",
315− "js-abort-controller": "1.0.0",
316− "js-bigints": "2.2.1",
317− "js-date": "8.0.0",
318− "js-fetch": "0.2.1",
319− "js-fileio": "3.0.0",
320− "js-intl": "1.1.4",
321− "js-iterators": "0.1.1",
322− "js-maps": "0.1.2",
323− "js-promise": "1.0.0",
324− "js-promise-aff": "1.0.0",
325− "js-timers": "6.1.0",
326− "js-uri": "3.1.0",
327− "jsdom": "1.0.0",
328− "json": "1.1.0",
329− "json-codecs": "5.0.0",
330− "justifill": "0.5.0",
331− "jwt": "0.0.9",
332− "labeled-data": "0.2.0",
333− "language-cst-parser": "0.14.1",
334− "lazy": "6.0.0",
335− "lazy-joe": "1.0.0",
336− "lcg": "4.0.0",
337− "leibniz": "5.0.0",
338− "leveldb": "1.0.1",
339− "liminal": "1.0.1",
340− "linalg": "6.0.0",
341− "lists": "7.0.0",
342− "literals": "1.0.2",
343− "logging": "3.0.0",
344− "logging-journald": "0.4.0",
345− "lumi-components": "18.0.0",
346− "machines": "7.0.0",
347− "maps-eager": "0.5.0",
348− "marionette": "1.0.0",
349− "marionette-react-basic-hooks": "0.1.1",
350− "marked": "0.1.0",
351− "matrices": "5.0.1",
352− "matryoshka": "1.0.0",
353− "maybe": "6.0.0",
354− "media-types": "6.0.0",
355− "meowclient": "1.0.0",
356− "midi": "4.0.0",
357− "milkis": "9.0.0",
358− "mimetype": "0.0.1",
359− "minibench": "4.0.1",
360− "mmorph": "7.0.0",
361− "monad-control": "5.0.0",
362− "monad-logger": "1.3.1",
363− "monad-loops": "0.5.0",
364− "monad-unlift": "1.0.1",
365− "monoid-extras": "0.0.1",
366− "monoidal": "0.16.0",
367− "morello": "0.4.0",
368− "mote": "3.0.0",
369− "motsunabe": "2.0.0",
370− "mvc": "0.0.1",
371− "mysql": "6.0.1",
372− "n3": "0.1.0",
373− "nano-id": "1.1.0",
374− "nanoid": "0.1.0",
375− "naturals": "3.0.0",
376− "nested-functor": "0.2.1",
377− "newtype": "5.0.0",
378− "nextjs": "0.1.1",
379− "nextui": "0.2.0",
380− "node-buffer": "9.0.0",
381− "node-child-process": "11.1.0",
382− "node-event-emitter": "3.0.0",
383− "node-execa": "5.0.0",
384− "node-fs": "9.2.0",
385− "node-glob-basic": "2.0.0",
386− "node-http": "9.1.0",
387− "node-http2": "1.1.1",
388− "node-human-signals": "1.0.0",
389− "node-net": "5.1.0",
390− "node-os": "5.1.0",
391− "node-path": "5.0.1",
392− "node-process": "11.2.0",
393− "node-readline": "8.1.1",
394− "node-sqlite3": "8.0.0",
395− "node-stream-pipes": "2.1.6",
396− "node-streams": "9.0.1",
397− "node-tls": "0.3.1",
398− "node-url": "7.0.1",
399− "node-workerbees": "0.3.1",
400− "node-zlib": "0.4.0",
401− "nonempty": "7.0.0",
402− "now": "6.0.0",
403− "npm-package-json": "2.0.0",
404− "nullable": "6.0.0",
405− "numberfield": "0.2.2",
406− "numbers": "9.0.1",
407− "oak": "3.1.1",
408− "oak-debug": "1.2.2",
409− "object-maps": "0.3.0",
410− "ocarina": "1.5.4",
411− "oooooooooorrrrrrrmm-lib": "0.0.1",
412− "open-colors-scales-and-schemes": "1.0.0",
413− "open-drawing": "6.0.4",
414− "open-folds": "6.4.0",
415− "open-foreign-generic": "11.0.3",
416− "open-memoize": "6.2.0",
417− "open-mkdirp-aff": "1.2.0",
418− "open-pairing": "6.2.0",
419− "open-smolder": "12.0.2",
420− "options": "7.0.0",
421− "optparse": "5.0.1",
422− "ordered-collections": "3.2.0",
423− "ordered-set": "0.4.0",
424− "orders": "6.0.0",
425− "org-doc": "0.1.0",
426− "owoify": "1.2.0",
427− "pairs": "9.0.1",
428− "parallel": "7.0.0",
429− "parsing": "11.0.0",
430− "parsing-dataview": "3.2.4",
431− "partial": "4.0.0",
432− "pathy": "9.0.0",
433− "pha": "0.13.0",
434− "phaser": "0.7.0",
435− "phylio": "1.1.2",
436− "pipes": "8.0.0",
437− "pirates-charm": "0.0.1",
438− "play": "1.0.0",
439− "pmock": "0.9.0",
440− "point-free": "1.0.0",
441− "pointed-list": "0.5.1",
442− "polymorphic-vectors": "4.0.0",
443− "posix-types": "6.0.0",
444− "postgresql": "2.0.21",
445− "precise": "6.0.0",
446− "precise-datetime": "7.0.0",
447− "prelude": "6.0.2",
448− "prettier-printer": "3.0.0",
449− "printf": "0.1.0",
450− "priority-queue": "0.1.2",
451− "profunctor": "6.0.1",
452− "profunctor-lenses": "8.0.0",
453− "protobuf": "4.4.0",
454− "psa-utils": "8.0.0",
455− "psci-support": "6.0.0",
456− "punycode": "1.0.0",
457− "pursfmt": "0.17.0",
458− "qualified-do": "2.2.0",
459− "quantities": "12.2.0",
460− "quickcheck": "8.0.1",
461− "quickcheck-combinators": "0.1.3",
462− "quickcheck-laws": "7.0.0",
463− "quickcheck-utf8": "0.0.0",
464− "random": "6.0.0",
465− "rationals": "6.0.0",
466− "rdf": "0.1.0",
467− "react": "11.0.0",
468− "react-aria": "0.2.0",
469− "react-basic": "17.0.0",
470− "react-basic-classic": "3.0.0",
471− "react-basic-dnd": "10.1.0",
472− "react-basic-dom": "7.0.0",
473− "react-basic-dom-beta": "0.1.1",
474− "react-basic-emotion": "7.1.0",
475− "react-basic-hooks": "9.0.0",
476− "react-basic-storybook": "2.0.0",
477− "react-dom": "8.0.0",
478− "react-halo": "3.0.0",
479− "react-icons": "1.1.5",
480− "react-markdown": "0.1.0",
481− "react-testing-library": "4.0.1",
482− "react-virtuoso": "1.0.0",
483− "reactix": "0.6.1",
484− "read": "1.0.1",
485− "recharts": "1.1.0",
486− "record": "4.0.0",
487− "record-extra": "5.0.1",
488− "record-extra-srghma": "0.2.8",
489− "record-ptional-fields": "0.1.2",
490− "record-studio": "1.0.4",
491− "refs": "6.0.0",
492− "remotedata": "5.0.1",
493− "repr": "0.5.0",
494− "resize-arrays": "0.0.1",
495− "resize-observer": "1.0.0",
496− "resource": "2.0.1",
497− "resourcet": "1.0.0",
498− "result": "1.0.3",
499− "return": "0.2.0",
500− "ring-modules": "5.0.1",
501− "rito": "0.3.4",
502− "roman": "0.4.0",
503− "rough-notation": "1.0.2",
504− "routing": "11.0.0",
505− "routing-duplex": "0.7.0",
506− "run": "5.0.0",
507− "safe-coerce": "2.0.0",
508− "safely": "4.0.1",
509− "school-of-music": "1.3.0",
510− "selection-foldable": "0.2.0",
511− "selective-functors": "1.0.1",
512− "semirings": "7.0.0",
513− "shuffle": "2.0.0",
514− "signal": "13.0.0",
515− "simple-emitter": "3.0.1",
516− "simple-i18n": "2.0.1",
517− "simple-json": "9.0.0",
518− "simple-json-generics": "0.2.1",
519− "simple-ulid": "3.0.0",
520− "sized-matrices": "1.0.0",
521− "sized-vectors": "5.0.2",
522− "slug": "3.2.0",
523− "small-ffi": "4.0.1",
524− "soundfonts": "4.1.0",
525− "sparse-matrices": "2.0.1",
526− "sparse-polynomials": "3.0.1",
527− "spec": "8.1.1",
528− "spec-discovery": "8.4.0",
529− "spec-mocha": "5.1.1",
530− "spec-node": "0.0.3",
531− "spec-quickcheck": "5.0.2",
532− "spec-reporter-xunit": "0.7.1",
533− "splitmix": "2.1.0",
534− "ssrs": "1.0.0",
535− "st": "6.2.0",
536− "statistics": "0.3.2",
537− "strictlypositiveint": "1.0.1",
538− "string-parsers": "8.0.0",
539− "strings": "6.0.1",
540− "strings-extra": "4.0.0",
541− "stringutils": "0.0.12",
542− "structured-logging": "1.0.0",
543− "substitute": "0.2.3",
544− "supply": "0.2.0",
545− "svg-parser": "3.0.0",
546− "systemd-journald": "0.3.0",
547− "tagged": "4.0.2",
548− "tailrec": "6.1.0",
549− "tanstack-query": "2.0.0",
550− "tecton": "0.2.1",
551− "tecton-halogen": "0.2.0",
552− "test-unit": "17.0.0",
553− "text-formatting": "0.1.0",
554− "thermite": "6.3.1",
555− "thermite-dom": "0.3.1",
556− "these": "6.0.0",
557− "threading": "0.0.3",
558− "tidy": "0.11.1",
559− "tidy-codegen": "4.0.1",
560− "tldr": "0.0.0",
561− "toestand": "0.9.0",
562− "transformation-matrix": "1.0.1",
563− "transformers": "6.1.0",
564− "transit": "1.0.0",
565− "tree-rose": "4.0.2",
566− "trivial-unfold": "0.5.0",
567− "ts-bridge": "4.0.0",
568− "tuples": "7.0.0",
569− "two-or-more": "1.0.0",
570− "type-equality": "4.0.1",
571− "typedenv": "2.0.1",
572− "typelevel": "6.0.0",
573− "typelevel-lists": "2.1.0",
574− "typelevel-peano": "1.0.1",
575− "typelevel-prelude": "7.0.0",
576− "typelevel-regex": "0.0.3",
577− "typelevel-rows": "0.1.0",
578− "typisch": "0.4.0",
579− "uint": "7.0.0",
580− "ulid": "3.0.1",
581− "uncurried-transformers": "1.1.0",
582− "undefined": "2.0.0",
583− "undefined-is-not-a-problem": "1.1.0",
584− "unfoldable": "6.0.0",
585− "unicode": "6.0.0",
586− "unique": "0.6.1",
587− "unlift": "1.0.1",
588− "unordered-collections": "3.1.0",
589− "unsafe-coerce": "6.0.0",
590− "unsafe-reference": "5.0.0",
591− "untagged-to-tagged": "0.1.4",
592− "untagged-union": "1.0.0",
593− "uri": "9.0.0",
594− "url-immutable": "1.0.0",
595− "url-regex-safe": "0.1.1",
596− "uuid": "9.0.0",
597− "uuidv4": "1.0.0",
598− "validation": "6.0.0",
599− "variant": "8.0.0",
600− "variant-encodings": "2.0.0",
601− "variant-gen": "1.0.0",
602− "vectorfield": "1.0.1",
603− "vectors": "2.1.0",
604− "versions": "7.0.0",
605− "visx": "0.0.2",
606− "vitest": "2.0.0",
607− "web-clipboard": "6.0.0",
608− "web-cssom": "2.0.0",
609− "web-cssom-view": "0.1.0",
610− "web-dom": "6.0.0",
611− "web-dom-parser": "8.0.0",
612− "web-dom-xpath": "3.0.0",
613− "web-encoding": "3.0.0",
614− "web-events": "4.0.0",
615− "web-fetch": "4.0.1",
616− "web-file": "4.0.0",
617− "web-geometry": "0.1.0",
618− "web-html": "4.1.1",
619− "web-pointerevents": "2.0.0",
620− "web-proletarian": "1.0.0",
621− "web-promise": "3.2.0",
622− "web-resize-observer": "2.1.0",
623− "web-router": "1.0.0",
624− "web-socket": "4.0.0",
625− "web-storage": "5.0.0",
626− "web-streams": "4.0.0",
627− "web-touchevents": "4.0.0",
628− "web-uievents": "5.0.0",
629− "web-url": "2.0.0",
630− "web-workers": "1.1.0",
631− "web-xhr": "5.0.1",
632− "webb-aff-list": "0.0.2",
633− "webb-array": "0.0.2",
634− "webb-channel": "0.0.3",
635− "webb-commandline": "0.0.3",
636− "webb-directory": "0.0.7",
637− "webb-file": "0.0.1",
638− "webb-map": "0.0.1",
639− "webb-monad": "0.0.2",
640− "webb-mutex": "0.0.7",
641− "webb-parsing": "0.0.5",
642− "webb-random": "0.0.1",
643− "webb-refer": "0.0.3",
644− "webb-set": "0.0.1",
645− "webb-slot": "0.0.1",
646− "webb-stateful": "0.0.3",
647− "webb-string": "0.0.1",
648− "webb-test": "0.0.1",
649− "webb-thread": "0.0.2",
650− "webb-writer": "0.0.3",
651− "webextension-polyfill": "0.1.0",
652− "webgpu": "0.0.1",
653− "which": "2.0.0",
654− "whine-core": "0.0.33",
655− "xterm": "1.0.0",
656− "yaml-next": "3.1.1",
657− "yoga-fetch": "1.0.1",
658− "yoga-json": "5.1.0",
659− "yoga-om": "0.1.0",
660− "yoga-postgres": "6.0.0",
661− "yoga-react-dom": "1.0.1",
662− "yoga-redis": "0.1.0",
663− "yoga-sql-types": "0.1.0",
664− "yoga-subtlecrypto": "0.1.0",
665− "yoga-test-docker": "0.1.0",
666− "yoga-tree": "1.0.0",
667− "yoga-tree-svg": "0.1.0",
668− "yoga-tree-utils": "1.0.0",
669− "z3": "0.0.2",
670− "zipperarray": "2.0.0"
671− }
672− },
673 "extra_packages": {}
97 "extra_packages": {}
674 },
98 },
675 "packages": {
99 "packages": {
@@ -1580,8 +1004,8 @@
1580 },
1004 },
1581 "spec": {
1005 "spec": {
1582 "type": "registry",
1006 "type": "registry",
1583− "version": "8.1.1",
1007+ "version": "8.1.2",
1584− "integrity": "sha256-b6qjDb7lUJGLV+WEoywLu6BCXC4zGpfbN43mTL+vBeI=",
1008+ "integrity": "sha256-klPPJ6lBQI1CGOAdLJ+ZoGRt8XZippkTKjn53BrSkL0=",
1585 "dependencies": [
1009 "dependencies": [
1586 "aff",
1010 "aff",
1587 "ansi",
1011 "ansi",
@@ -1807,10 +1231,11 @@
1807 },
1231 },
1808 "yoga-om": {
1232 "yoga-om": {
1809 "type": "registry",
1233 "type": "registry",
1810− "version": "0.1.0",
1234+ "version": "2.0.0",
1811− "integrity": "sha256-0EGWo/UOVLzZ14DPtdv0C+bTvNO49wNgtTBIXI6QHqU=",
1235+ "integrity": "sha256-eenoujD2rJzR6gYJbbIiqLhXHC1C5dagu91HZkAEt/Y=",
1812 "dependencies": [
1236 "dependencies": [
1813 "aff",
1237 "aff",
1238+ "aff-retry",
1814 "avar",
1239 "avar",
1815 "console",
1240 "console",
1816 "control",
1241 "control",
@@ -1819,12 +1244,14 @@
1819 "either",
1244 "either",
1820 "exceptions",
1245 "exceptions",
1821 "functions",
1246 "functions",
1247+ "heterogeneous",
1822 "maybe",
1248 "maybe",
1823 "newtype",
1249 "newtype",
1824 "parallel",
1250 "parallel",
1825 "prelude",
1251 "prelude",
1826 "record",
1252 "record",
1827 "record-studio",
1253 "record-studio",
1254+ "refs",
1828 "tailrec",
1255 "tailrec",
1829 "transformers",
1256 "transformers",
1830 "tuples",
1257 "tuples",
spago.yaml+6−6
@@ -1,11 +1,8 @@
1−workspace:
1+workspace: {}
2− packageSet:
3− registry: 73.0.0
4−
5package:
2package:
6 name: yoga-om-layer
3 name: yoga-om-layer
7 publish:
4 publish:
8− version: 1.1.0
5+ version: 2.1.0
9 license: MIT
6 license: MIT
10 location:
7 location:
11 githubOwner: rowtype-yoga
8 githubOwner: rowtype-yoga
@@ -14,6 +11,7 @@ package:
14 - aff: ">=8.0.0 <9.0.0"
11 - aff: ">=8.0.0 <9.0.0"
15 - aff-retry: ">=2.0.0 <3.0.0"
12 - aff-retry: ">=2.0.0 <3.0.0"
16 - arrays: ">=7.3.0 <8.0.0"
13 - arrays: ">=7.3.0 <8.0.0"
14+ - control: ">=6.0.0 <7.0.0"
17 - effect: ">=4.0.0 <5.0.0"
15 - effect: ">=4.0.0 <5.0.0"
18 - exceptions: ">=6.1.0 <7.0.0"
16 - exceptions: ">=6.1.0 <7.0.0"
19 - foldable-traversable: ">=6.0.0 <7.0.0"
17 - foldable-traversable: ">=6.0.0 <7.0.0"
@@ -25,11 +23,13 @@ package:
25 - record: ">=4.0.0 <5.0.0"
23 - record: ">=4.0.0 <5.0.0"
26 - record-studio: ">=1.0.4 <2.0.0"
24 - record-studio: ">=1.0.4 <2.0.0"
27 - refs: ">=6.0.0 <7.0.0"
25 - refs: ">=6.0.0 <7.0.0"
26+ - tailrec: ">=6.1.0 <7.0.0"
28 - transformers: ">=6.1.0 <7.0.0"
27 - transformers: ">=6.1.0 <7.0.0"
29 - type-equality: ">=4.0.1 <5.0.0"
28 - type-equality: ">=4.0.1 <5.0.0"
29+ - typelevel-prelude: ">=7.0.0 <8.0.0"
30 - unsafe-coerce: ">=6.0.0 <7.0.0"
30 - unsafe-coerce: ">=6.0.0 <7.0.0"
31 - variant: ">=8.0.0 <9.0.0"
31 - variant: ">=8.0.0 <9.0.0"
32− - yoga-om: ">=0.1.0 <0.2.0"
32+ - yoga-om: ">=2.0.0 <3.0.0"
33 test:
33 test:
34 main: Test.Main
34 main: Test.Main
35 dependencies:
35 dependencies:
src/Yoga/Om/Layer.purs+398−32
@@ -13,33 +13,59 @@ module Yoga.Om.Layer
13 , runScopedWith
13 , runScopedWith
14 , withScoped
14 , withScoped
15 , withScopedWith
15 , withScopedWith
16+ , scoped
16 , combineRequirements
17 , combineRequirements
18+ , withValue
17 , provide
19 , provide
18 , (>->)
20 , (>->)
19 , recovering
21 , recovering
20 , repeating
22 , repeating
21 , wireLayers
23 , wireLayers
22 , wireLayersDebug
24 , wireLayersDebug
23− , wireLayersRL
24− , class WireLayersRL
25 , class FilterScope
25 , class FilterScope
26 , class FindProvider
26 , class FindProvider
27 , class FindProviderMatch
27 , class FindProviderMatch
28 , class HasLabel
28 , class HasLabel
29 , class EmitEdges
29 , class EmitEdges
30+ , class DashesFor
31+ , class BoxTop, class BoxMid, class BoxBot
32+ , class RenderDepChildNode
33+ , class IsNotProvided
34+ , class HasDirectMissing, class HasDirectMissingOr
35+ , class FindLayerReqs
36+ , class RenderProviderDeps
37+ , class RenderDepTree
38+ , class FirstProvLabel
39+ , class PrintLayerDispatch
30 , class PrintLayersRL
40 , class PrintLayersRL
31 , class PrintEdgesRL
41 , class PrintEdgesRL
42+ , class PrintLayersASCII
43+ , class PrintEdgesASCII
44+ , NotProvided
32 , class CheckAllProvided
45 , class CheckAllProvided
33 , class CheckAllLabelsExist
46 , class CheckAllLabelsExist
34 , class CheckLabelExists
47 , class CheckLabelExists
48+ , class TopoWire, topoWire
49+ , class AllLabelsIn
50+ , class FindReady
51+ , class FindReadyDispatch
35 ) where
52 ) where
36
53
37import Prelude
54import Prelude
38
55
56+import Control.Alt (class Alt)
57+import Control.Alt as Alt
58+import Control.Alternative (class Alternative)
39−import Control.Monad.Error.Class (catchError, throwError)
59+import Control.Monad.Error.Class (class MonadError, class MonadThrow, catchError, throwError)
40−import Data.Symbol (class IsSymbol)
60+import Control.Monad.Reader (class MonadAsk, class MonadReader)
61+import Control.Monad.Reader as Reader
62+import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM)
41import Control.Parallel (class Parallel)
63import Control.Parallel (class Parallel)
42import Control.Parallel as Parallel
64import Control.Parallel as Parallel
65+import Control.Plus (class Plus)
66+import Control.Plus as Plus
67+import Data.Symbol (class IsSymbol)
68+import Data.Variant (Variant)
43import Data.Array as Array
69import Data.Array as Array
44import Data.Foldable (for_)
70import Data.Foldable (for_)
45import Data.Map (Map)
71import Data.Map (Map)
@@ -50,7 +76,10 @@ import Data.Variant (class VariantMatchCases, onMatch)
50import Effect (Effect)
76import Effect (Effect)
51import Effect.Aff (Aff, bracket)
77import Effect.Aff (Aff, bracket)
52import Effect.Aff.Retry (RetryPolicyM, RetryStatus, applyAndDelay, defaultRetryStatus)
78import Effect.Aff.Retry (RetryPolicyM, RetryStatus, applyAndDelay, defaultRetryStatus)
79+import Effect.Aff.Class (class MonadAff)
80+import Effect.Aff.Class as Aff
53−import Effect.Class (liftEffect)
81+import Effect.Class (class MonadEffect, liftEffect)
82+import Effect.Class (class MonadEffect, liftEffect) as Effect
54import Effect.Exception (Error)
83import Effect.Exception (Error)
55import Effect.Ref (Ref)
84import Effect.Ref (Ref)
56import Effect.Ref as Ref
85import Effect.Ref as Ref
@@ -60,14 +89,17 @@ import Prim.Row as Row
60import Type.Proxy (Proxy(..))
89import Type.Proxy (Proxy(..))
61import Prim.RowList (class RowToList, Cons, Nil, RowList)
90import Prim.RowList (class RowToList, Cons, Nil, RowList)
62import Prim.Boolean (True, False)
91import Prim.Boolean (True, False)
63−import Prim.Symbol (class Append)
92+import Prim.Symbol (class Append, class Cons)
93+import Type.Data.Boolean (class And)
94+import Type.RowList (class RowListAppend)
64−import Prim.TypeError (class Fail, class Warn, Above, Doc, Quote, Text)
95+import Prim.TypeError (class Fail, class Warn, Above, Beside, Doc, Quote, Text)
65import Record as Record
96import Record as Record
66import Record.Studio (class Keys)
97import Record.Studio (class Keys)
67import Type.Equality (class TypeEquals)
98import Type.Equality (class TypeEquals)
68import Unsafe.Coerce (unsafeCoerce)
99import Unsafe.Coerce (unsafeCoerce)
69import Yoga.Om (Om, ParOm)
100import Yoga.Om (Om, ParOm)
70import Yoga.Om as Om
101import Yoga.Om as Om
102+import Yoga.Om.Error (Exception)
71
103
72-- =============================================================================
104-- =============================================================================
73-- Scope — a first-class representation of resource lifetime
105-- Scope — a first-class representation of resource lifetime
@@ -156,6 +188,36 @@ instance Bind (OmLayer req err) where
156
188
157instance Monad (OmLayer req err)
189instance Monad (OmLayer req err)
158
190
191+instance MonadEffect (OmLayer req err) where
192+ liftEffect eff = OmLayer (freshId unit) (Effect.liftEffect eff)
193+
194+instance MonadAff (OmLayer req err) where
195+ liftAff aff = OmLayer (freshId unit) (Aff.liftAff aff)
196+
197+instance MonadAsk (Record req) (OmLayer req err) where
198+ ask = OmLayer (freshId unit) Reader.ask
199+
200+instance MonadReader (Record req) (OmLayer req err) where
201+ local f layer = OmLayer (freshId unit) (Reader.local f (buildLayer layer))
202+
203+instance MonadThrow (Variant (Exception err)) (OmLayer req err) where
204+ throwError e = OmLayer (freshId unit) (throwError e)
205+
206+instance MonadError (Variant (Exception err)) (OmLayer req err) where
207+ catchError layer handler = OmLayer (freshId unit) (catchError (buildLayer layer) (\e -> buildLayer (handler e)))
208+
209+instance MonadRec (OmLayer req err) where
210+ tailRecM f a = OmLayer (freshId unit) (tailRecM (\x -> buildLayer (f x)) a)
211+
212+instance Semigroup a => Semigroup (OmLayer req err a) where
213+ append a b = OmLayer (freshId unit) (append (buildLayer a) (buildLayer b))
214+
215+instance Alt (OmLayer req err) where
216+ alt a b = OmLayer (freshId unit) (Alt.alt (buildLayer a) (buildLayer b))
217+
218+instance Plus (OmLayer req err) where
219+ empty = OmLayer (freshId unit) Plus.empty
220+
159instance Functor (ParOmLayer req err) where
221instance Functor (ParOmLayer req err) where
160 map f (ParOmLayer p) = ParOmLayer (map f p)
222 map f (ParOmLayer p) = ParOmLayer (map f p)
161
223
@@ -165,6 +227,14 @@ instance Apply (ParOmLayer req err) where
165instance Applicative (ParOmLayer req err) where
227instance Applicative (ParOmLayer req err) where
166 pure a = ParOmLayer (pure a)
228 pure a = ParOmLayer (pure a)
167
229
230+instance Alt (ParOmLayer req err) where
231+ alt (ParOmLayer a) (ParOmLayer b) = ParOmLayer (Alt.alt a b)
232+
233+instance Plus (ParOmLayer req err) where
234+ empty = ParOmLayer Plus.empty
235+
236+instance Alternative (ParOmLayer req err)
237+
168instance Parallel (ParOmLayer req err) (OmLayer req err) where
238instance Parallel (ParOmLayer req err) (OmLayer req err) where
169 parallel (OmLayer _ om) = ParOmLayer (Parallel.parallel om)
239 parallel (OmLayer _ om) = ParOmLayer (Parallel.parallel om)
170 sequential (ParOmLayer p) = OmLayer (freshId unit) (Parallel.sequential p)
240 sequential (ParOmLayer p) = OmLayer (freshId unit) (Parallel.sequential p)
@@ -322,9 +392,13 @@ else instance
322-- =============================================================================
392-- =============================================================================
323
393
324-- | Run a layer with a given context, with custom error if requirements aren't met.
394-- | Run a layer with a given context, with custom error if requirements aren't met.
395+-- | `runLayer` requires a closed requirement row. If you have an open layer like
396+-- | `OmLayer (db :: Db | r) err a`, specialize it to a closed row before running
397+-- | it directly.
325runLayer
398runLayer
326 :: forall req err a available
399 :: forall req err a available
327 . CheckAllProvided req available
400 . CheckAllProvided req available
401+ => Keys req
328 => Record available
402 => Record available
329 -> OmLayer req err a
403 -> OmLayer req err a
330 -> Om (Record available) err a
404 -> Om (Record available) err a
@@ -352,7 +426,7 @@ withScoped
352 . OmLayer (scope :: Scope) () (Record prov)
426 . OmLayer (scope :: Scope) () (Record prov)
353 -> (Record prov -> Aff a)
427 -> (Record prov -> Aff a)
354 -> Aff a
428 -> Aff a
355−withScoped = withScopedWith { exception: \_ -> pure (unsafeCoerce {}) }
429+withScoped = withScopedWith { exception: throwError }
356
430
357-- | Like `runScoped` but accepts error handlers for layers with typed errors.
431-- | Like `runScoped` but accepts error handlers for layers with typed errors.
358runScopedWith
432runScopedWith
@@ -385,6 +459,17 @@ withScopedWith handlers layer callback = bracket acquire release use
385 release { scope } = closeScope scope
459 release { scope } = closeScope scope
386 use { prov } = callback prov
460 use { prov } = callback prov
387
461
462+scoped
463+ :: forall prov a r rl err_ err
464+ . RowToList (exception :: Error -> Aff (Record prov) | r) rl
465+ => VariantMatchCases rl err_ (Aff (Record prov))
466+ => Union err_ () (exception :: Error | err)
467+ => { exception :: Error -> Aff (Record prov) | r }
468+ -> OmLayer (scope :: Scope) err (Record prov)
469+ -> (Record prov -> Aff a)
470+ -> Aff a
471+scoped = withScopedWith
472+
388-- =============================================================================
473-- =============================================================================
389-- Horizontal composition — combine two layers with automatic deduplication
474-- Horizontal composition — combine two layers with automatic deduplication
390-- =============================================================================
475-- =============================================================================
@@ -411,6 +496,17 @@ combineRequirements layer1 layer2 = OmLayer (freshId unit) do
411 rec2 <- Om.expand (buildLayer layer2)
496 rec2 <- Om.expand (buildLayer layer2)
412 pure (Record.merge rec1 rec2)
497 pure (Record.merge rec1 rec2)
413
498
499+withValue
500+ :: forall req err prov1 prov2 provMerged
501+ . Union prov1 prov2 provMerged
502+ => Nub provMerged provMerged
503+ => Record prov1
504+ -> OmLayer req err (Record prov2)
505+ -> OmLayer req err (Record provMerged)
506+withValue defaults layer = OmLayer (freshId unit) do
507+ rec <- buildLayer layer
508+ pure (Record.merge defaults rec)
509+
414-- =============================================================================
510-- =============================================================================
415-- Vertical composition — feed output of one layer into input of another
511-- Vertical composition — feed output of one layer into input of another
416-- =============================================================================
512-- =============================================================================
@@ -481,13 +577,82 @@ repeating policy shouldRepeat (OmLayer layerId om) =
481 (applyAndDelay policy status >>= maybe (pure result) go)
577 (applyAndDelay policy status >>= maybe (pure result) go)
482 (pure result)
578 (pure result)
483
579
580+
484-- =============================================================================
581-- =============================================================================
485−-- Auto-wiring
582+-- Topological auto-wiring (order-independent)
486-- =============================================================================
583-- =============================================================================
487
584
488−class
585+-- | Check if ALL labels in `needles` exist in `haystack`, returning a Boolean.
489− WireLayersRL
586+class AllLabelsIn (needles :: RowList Type) (haystack :: RowList Type) (result :: Boolean)
587+ | needles haystack -> result
588+
589+instance AllLabelsIn Nil haystack True
590+
591+instance
592+ ( HasLabel label haystack found
593+ , AllLabelsIn tail haystack tailResult
594+ , And found tailResult result
595+ ) =>
596+ AllLabelsIn (Cons label ty tail) haystack result
597+
598+-- | Dispatch based on whether a layer is ready (deps satisfied).
599+class FindReadyDispatch
600+ (ready :: Boolean)
601+ (sym :: Symbol)
602+ (layer :: Type)
603+ (tail :: RowList Type)
604+ (available :: RowList Type)
605+ (allLayersRL :: RowList Type)
606+ (foundSym :: Symbol)
607+ (foundLayer :: Type)
608+ (rest :: RowList Type)
609+ | ready sym layer tail available allLayersRL -> foundSym foundLayer rest
610+
611+instance FindReadyDispatch True sym layer tail available allLayersRL sym layer tail
612+
613+else instance
614+ FindReady tail available allLayersRL foundSym foundLayer rest' =>
615+ FindReadyDispatch False sym layer tail available allLayersRL foundSym foundLayer (Cons sym layer rest')
616+
617+-- | Scan candidates for the first layer whose requirements are satisfied.
618+class FindReady
619+ (candidates :: RowList Type)
620+ (available :: RowList Type)
621+ (allLayersRL :: RowList Type)
622+ (sym :: Symbol)
623+ (layer :: Type)
624+ (rest :: RowList Type)
625+ | candidates available allLayersRL -> sym layer rest
626+
627+instance
628+ ( PrintLayersASCII allLayersRL graphDoc
629+ , Fail
630+ ( Above (Text "")
631+ ( Above (Text "Dependency not provided!")
632+ ( Above (Text "")
633+ ( Above (Text "No remaining layer has all its dependencies satisfied.")
634+ ( Above (Text "")
635+ ( Above (Text "Dependency graph:") graphDoc
636+ )
637+ )
638+ )
639+ )
640+ )
641+ )
642+ ) =>
643+ FindReady Nil available allLayersRL sym layer rest
644+
645+else instance
646+ ( RowToList req reqRL
647+ , AllLabelsIn reqRL available isReady
648+ , FindReadyDispatch isReady sym (OmLayer req err (Record prov)) tail available allLayersRL foundSym foundLayer rest
649+ ) =>
650+ FindReady (Cons sym (OmLayer req err (Record prov)) tail) available allLayersRL foundSym foundLayer rest
651+
652+-- | Topologically sort and wire layers. Each iteration finds a layer whose
653+-- | dependencies are already satisfied, processes it, and adds its provisions.
654+class TopoWire
490− (rl :: RowList Type)
655+ (remaining :: RowList Type)
491 (layers :: Row Type)
656 (layers :: Row Type)
492 (accReq :: Row Type)
657 (accReq :: Row Type)
493 (accErr :: Row Type)
658 (accErr :: Row Type)
@@ -495,20 +660,25 @@ class
495 (resReq :: Row Type)
660 (resReq :: Row Type)
496 (resErr :: Row Type)
661 (resErr :: Row Type)
497 (resProv :: Row Type)
662 (resProv :: Row Type)
498− | rl layers accReq accErr accProv -> resReq resErr resProv
663+ | remaining layers accReq accErr accProv -> resReq resErr resProv
499 where
664 where
500− wireLayersRL
665+ topoWire
501− :: Proxy rl
666+ :: Proxy remaining
502 -> Record layers
667 -> Record layers
503 -> OmLayer accReq accErr (Record accProv)
668 -> OmLayer accReq accErr (Record accProv)
504 -> OmLayer resReq resErr (Record resProv)
669 -> OmLayer resReq resErr (Record resProv)
505
670
506−instance WireLayersRL Nil layers accReq accErr accProv accReq accErr accProv where
671+instance TopoWire Nil layers accReq accErr accProv accReq accErr accProv where
507− wireLayersRL _ _ acc = acc
672+ topoWire _ _ acc = acc
508
673
509−instance
674+else instance
675+ ( RowToList layers allLayersRL
676+ , RowToList accProv accProvRL
677+ , RowToList accReq accReqRL
678+ , RowListAppend accProvRL accReqRL availableRL
679+ , FindReady remaining availableRL allLayersRL readySym (OmLayer nextReq nextErr (Record nextProv)) rest
510− ( IsSymbol sym
680+ , IsSymbol readySym
511− , Row.Cons sym (OmLayer nextReq nextErr (Record nextProv)) _rest layers
681+ , Row.Cons readySym (OmLayer nextReq nextErr (Record nextProv)) _rest layers
512 -- next's requirements must be subset of (accProv + accReq)
682 -- next's requirements must be subset of (accProv + accReq)
513 , Union accProv accReq provReqOut
683 , Union accProv accReq provReqOut
514 , Nub provReqOut provReqOut
684 , Nub provReqOut provReqOut
@@ -521,19 +691,18 @@ instance
521 , Nub errMergedRaw errMerged
691 , Nub errMergedRaw errMerged
522 , Union accErr _e1 errMerged
692 , Union accErr _e1 errMerged
523 , Union nextErr _e2 errMerged
693 , Union nextErr _e2 errMerged
524− -- merge provisions (Nub provMerged provMerged ensures disjoint)
694+ -- merge provisions
525 , Union accProv nextProv provMerged
695 , Union accProv nextProv provMerged
526 , Nub provMerged provMerged
696 , Nub provMerged provMerged
527 -- recurse
697 -- recurse
528− , WireLayersRL tail layers accReq errMerged provMerged resReq resErr resProv
698+ , TopoWire rest layers accReq errMerged provMerged resReq resErr resProv
529 ) =>
699 ) =>
530− WireLayersRL (Cons sym (OmLayer nextReq nextErr (Record nextProv)) tail) layers accReq accErr accProv resReq resErr resProv
700+ TopoWire remaining layers accReq accErr accProv resReq resErr resProv where
531− where
532− wireLayersRL _ layers acc =
701+ topoWire _ layers acc =
533− wireLayersRL (Proxy :: Proxy tail) layers combined
702+ topoWire (Proxy :: Proxy rest) layers combined
534 where
703 where
535 next :: OmLayer nextReq nextErr (Record nextProv)
704 next :: OmLayer nextReq nextErr (Record nextProv)
536− next = Record.get (Proxy :: Proxy sym) layers
705+ next = Record.get (Proxy :: Proxy readySym) layers
537
706
538 combined :: OmLayer accReq errMerged (Record provMerged)
707 combined :: OmLayer accReq errMerged (Record provMerged)
539 combined = OmLayer (freshId unit) do
708 combined = OmLayer (freshId unit) do
@@ -544,11 +713,11 @@ instance
544wireLayers
713wireLayers
545 :: forall layers rl req err prov
714 :: forall layers rl req err prov
546 . RowToList layers rl
715 . RowToList layers rl
547− => WireLayersRL rl layers (scope :: Scope) () () req err prov
716+ => TopoWire rl layers (scope :: Scope) () () req err prov
548 => Record layers
717 => Record layers
549 -> OmLayer req err (Record prov)
718 -> OmLayer req err (Record prov)
550wireLayers layers =
719wireLayers layers =
551− wireLayersRL (Proxy :: Proxy rl) layers seed
720+ topoWire (Proxy :: Proxy rl) layers seed
552 where
721 where
553 seed = pure {} :: OmLayer (scope :: Scope) () (Record ())
722 seed = pure {} :: OmLayer (scope :: Scope) () (Record ())
554
723
@@ -563,10 +732,16 @@ instance FilterScope Nil Nil
563instance FilterScope (Cons "scope" Scope tail) tail
732instance FilterScope (Cons "scope" Scope tail) tail
564else instance FilterScope tail out => FilterScope (Cons sym ty tail) (Cons sym ty out)
733else instance FilterScope tail out => FilterScope (Cons sym ty tail) (Cons sym ty out)
565
734
735+-- | Marker symbol for unresolved dependencies.
736+type NotProvided :: Symbol
737+type NotProvided = "???"
738+
566-- | Given a label, find which layer in the RowList provides it.
739-- | Given a label, find which layer in the RowList provides it.
567class FindProvider (label :: Symbol) (rl :: RowList Type) (provider :: Symbol) | label rl -> provider
740class FindProvider (label :: Symbol) (rl :: RowList Type) (provider :: Symbol) | label rl -> provider
568
741
569−instance
742+instance FindProvider label Nil NotProvided
743+
744+else instance
570 ( RowToList prov provRL
745 ( RowToList prov provRL
571 , HasLabel label provRL hasIt
746 , HasLabel label provRL hasIt
572 , FindProviderMatch hasIt label sym (Cons sym (OmLayer req err (Record prov)) tail) provider
747 , FindProviderMatch hasIt label sym (Cons sym (OmLayer req err (Record prov)) tail) provider
@@ -603,7 +778,7 @@ else instance
603 ) =>
778 ) =>
604 EmitEdges consumer (Cons label ty tail) allLayers (Above (Text line) restDoc)
779 EmitEdges consumer (Cons label ty tail) allLayers (Above (Text line) restDoc)
605
780
606−-- | Walk all layers emitting D2 edges.
781+-- | Walk all layers emitting D2 edges (mermaid format for wireLayersDebug).
607class PrintLayersRL (rl :: RowList Type) (doc :: Doc) | rl -> doc
782class PrintLayersRL (rl :: RowList Type) (doc :: Doc) | rl -> doc
608class PrintEdgesRL (rl :: RowList Type) (allLayers :: RowList Type) (doc :: Doc) | rl allLayers -> doc
783class PrintEdgesRL (rl :: RowList Type) (allLayers :: RowList Type) (doc :: Doc) | rl allLayers -> doc
609
784
@@ -620,12 +795,203 @@ else instance
620 ) =>
795 ) =>
621 PrintEdgesRL (Cons sym (OmLayer req err (Record prov)) tail) allLayers (Above edgeDoc restDoc)
796 PrintEdgesRL (Cons sym (OmLayer req err (Record prov)) tail) allLayers (Above edgeDoc restDoc)
622
797
798+-- | Map a symbol to same-length dashes (for box borders).
799+class DashesFor (sym :: Symbol) (dashes :: Symbol) | sym -> dashes
800+
801+instance DashesFor "" ""
802+
803+else instance
804+ ( Cons head tail sym
805+ , DashesFor tail rest
806+ , Append "─" rest dashes
807+ ) =>
808+ DashesFor sym dashes
809+
810+-- | Build box parts from a symbol.
811+class BoxTop (sym :: Symbol) (result :: Symbol) | sym -> result
812+
813+instance
814+ ( DashesFor sym dashes
815+ , Append "╭─" dashes s0
816+ , Append s0 "─╮" result
817+ ) =>
818+ BoxTop sym result
819+
820+class BoxMid (sym :: Symbol) (result :: Symbol) | sym -> result
821+
822+instance
823+ ( Append "│ " sym s0
824+ , Append s0 " │" result
825+ ) =>
826+ BoxMid sym result
827+
828+class BoxBot (sym :: Symbol) (result :: Symbol) | sym -> result
829+
830+instance
831+ ( DashesFor sym dashes
832+ , Append "╰─" dashes s0
833+ , Append s0 "─╯" result
834+ ) =>
835+ BoxBot sym result
836+
837+-- | Render a single dependency child node (dispatches on provided vs missing).
838+class RenderDepChildNode
839+ (isNotProvided :: Boolean) (provider :: Symbol) (label :: Symbol) (ty :: Type)
840+ (prePrefix :: Symbol) (contPrefix :: Symbol) (branchPrefix :: Symbol)
841+ (allLayers :: RowList Type) (doc :: Doc)
842+ | isNotProvided provider label ty prePrefix contPrefix branchPrefix allLayers -> doc
843+
844+-- Missing dep: boxed label, type outside
845+instance
846+ ( BoxTop label topLine, BoxMid label midLine, BoxBot label botLine
847+ , Append prePrefix topLine topFull
848+ , Append branchPrefix midLine midFull
849+ , Append contPrefix botLine botFull
850+ ) =>
851+ RenderDepChildNode True provider label ty prePrefix contPrefix branchPrefix allLayers
852+ (Above (Text topFull) (Above (Beside (Text midFull) (Beside (Text " :: ") (Beside (Quote ty) (Text " <-- ⚠️ not provided")))) (Text botFull)))
853+
854+-- Provided dep: boxed label, then recurse into provider's sub-tree
855+instance
856+ ( BoxTop label topLine, BoxMid label midLine, BoxBot label botLine
857+ , Append prePrefix topLine topFull
858+ , Append branchPrefix midLine midFull
859+ , Append contPrefix botLine botFull
860+ , Append contPrefix " " subTreePrefix
861+ , RenderProviderDeps provider subTreePrefix allLayers subDoc
862+ ) =>
863+ RenderDepChildNode False provider label ty prePrefix contPrefix branchPrefix allLayers
864+ (Above (Text topFull) (Above (Text midFull) (Above (Text botFull) subDoc)))
865+
866+-- | Is this provider the NotProvided marker?
867+class IsNotProvided (provider :: Symbol) (result :: Boolean) | provider -> result
868+instance IsNotProvided NotProvided True
869+else instance IsNotProvided other False
870+
871+-- | Does any requirement in this RowList resolve to NotProvided?
872+class HasDirectMissing (reqRL :: RowList Type) (allLayers :: RowList Type) (result :: Boolean)
873+ | reqRL allLayers -> result
874+
875+instance HasDirectMissing Nil allLayers False
876+
877+else instance
878+ ( FindProvider label allLayers provider
879+ , IsNotProvided provider isMissing
880+ , HasDirectMissingOr isMissing tail allLayers result
881+ ) =>
882+ HasDirectMissing (Cons label ty tail) allLayers result
883+
884+class HasDirectMissingOr (found :: Boolean) (tail :: RowList Type) (allLayers :: RowList Type) (result :: Boolean)
885+ | found tail allLayers -> result
886+
887+instance HasDirectMissingOr True tail allLayers True
888+
889+else instance
890+ HasDirectMissing tail allLayers result =>
891+ HasDirectMissingOr False tail allLayers result
892+
893+-- | Look up a layer's requirements by name.
894+class FindLayerReqs (sym :: Symbol) (allLayers :: RowList Type) (reqRL :: RowList Type)
895+ | sym allLayers -> reqRL
896+
897+instance
898+ RowToList req reqRL =>
899+ FindLayerReqs sym (Cons sym (OmLayer req err (Record prov)) tail) reqRL
900+
901+else instance
902+ FindLayerReqs sym tail reqRL =>
903+ FindLayerReqs sym (Cons other otherLayer tail) reqRL
904+
905+-- | Render a provider's sub-tree (stops at NotProvided or roots).
906+class RenderProviderDeps
907+ (provider :: Symbol) (prefix :: Symbol) (allLayers :: RowList Type) (doc :: Doc)
908+ | provider prefix allLayers -> doc
909+
910+instance RenderProviderDeps NotProvided prefix allLayers (Text "")
911+
912+else instance
913+ ( FindLayerReqs provider allLayers reqRL
914+ , FilterScope reqRL filteredReqRL
915+ , RenderDepTree prefix filteredReqRL allLayers doc
916+ ) =>
917+ RenderProviderDeps provider prefix allLayers doc
918+
919+-- | Render a dependency tree with tree connectors.
920+class RenderDepTree
921+ (prefix :: Symbol) (reqRL :: RowList Type) (allLayers :: RowList Type) (doc :: Doc)
922+ | prefix reqRL allLayers -> doc
923+
924+instance RenderDepTree prefix Nil allLayers (Text "")
925+
926+-- Last child: └───
927+else instance
928+ ( FindProvider label allLayers provider
929+ , IsNotProvided provider isNP
930+ , Append prefix "│ " prePrefix
931+ , Append prefix " " contPrefix
932+ , Append prefix "└───" branchPrefix
933+ , RenderDepChildNode isNP provider label ty prePrefix contPrefix branchPrefix allLayers doc
934+ ) =>
935+ RenderDepTree prefix (Cons label ty Nil) allLayers doc
936+
937+-- Non-last child: ├───
938+else instance
939+ ( FindProvider label allLayers provider
940+ , IsNotProvided provider isNP
941+ , Append prefix "│ " contPrefix
942+ , Append prefix "├───" branchPrefix
943+ , RenderDepChildNode isNP provider label ty contPrefix contPrefix branchPrefix allLayers childDoc
944+ , RenderDepTree prefix tail allLayers restDoc
945+ ) =>
946+ RenderDepTree prefix (Cons label ty tail) allLayers (Above childDoc restDoc)
947+
948+-- | Extract the first label from a RowList.
949+class FirstProvLabel (rl :: RowList Type) (label :: Symbol) | rl -> label
950+instance FirstProvLabel (Cons label ty tail) label
951+
952+-- | Dispatch: skip layers without missing deps, render tree for others.
953+class PrintLayerDispatch
954+ (show :: Boolean) (displayName :: Symbol) (filteredReqRL :: RowList Type)
955+ (tail :: RowList Type) (allLayers :: RowList Type) (doc :: Doc)
956+ | show displayName filteredReqRL tail allLayers -> doc
957+
958+instance
959+ PrintEdgesASCII tail allLayers doc =>
960+ PrintLayerDispatch False displayName filteredReqRL tail allLayers doc
961+
962+else instance
963+ ( BoxTop displayName topLine, BoxMid displayName midLine, BoxBot displayName botLine
964+ , RenderDepTree " " filteredReqRL allLayers treeDoc
965+ , PrintEdgesASCII tail allLayers restDoc
966+ ) =>
967+ PrintLayerDispatch True displayName filteredReqRL tail allLayers
968+ (Above (Text topLine) (Above (Text midLine) (Above (Text botLine) (Above treeDoc (Above (Text "") restDoc)))))
969+
970+-- | Walk all layers emitting ASCII dependency trees (for error messages).
971+class PrintLayersASCII (rl :: RowList Type) (doc :: Doc) | rl -> doc
972+class PrintEdgesASCII (rl :: RowList Type) (allLayers :: RowList Type) (doc :: Doc) | rl allLayers -> doc
973+
974+instance PrintLayersASCII Nil (Text "")
975+else instance PrintEdgesASCII rl rl doc => PrintLayersASCII rl doc
976+
977+instance PrintEdgesASCII Nil allLayers (Text "")
978+
979+else instance
980+ ( RowToList req reqRL
981+ , FilterScope reqRL filteredReqRL
982+ , RowToList prov provRL
983+ , FirstProvLabel provRL displayName
984+ , HasDirectMissing filteredReqRL allLayers hasMissing
985+ , PrintLayerDispatch hasMissing displayName filteredReqRL tail allLayers doc
986+ ) =>
987+ PrintEdgesASCII (Cons sym (OmLayer req err (Record prov)) tail) allLayers doc
988+
623wireLayersDebug
989wireLayersDebug
624 :: forall layers rl req err prov doc
990 :: forall layers rl req err prov doc
625 . RowToList layers rl
991 . RowToList layers rl
626 => PrintLayersRL rl doc
992 => PrintLayersRL rl doc
627 => Warn (Above (Text "") (Above (Text "%%{init: {\"flowchart\": {\"defaultRenderer\": \"elk\"}} }%%") (Above (Text "flowchart LR") doc)))
993 => Warn (Above (Text "") (Above (Text "%%{init: {\"flowchart\": {\"defaultRenderer\": \"elk\"}} }%%") (Above (Text "flowchart LR") doc)))
628− => WireLayersRL rl layers (scope :: Scope) () () req err prov
994+ => TopoWire rl layers (scope :: Scope) () () req err prov
629 => Record layers
995 => Record layers
630 -> OmLayer req err (Record prov)
996 -> OmLayer req err (Record prov)
631wireLayersDebug = wireLayers
997wireLayersDebug = wireLayers
test/Test/Yoga/Om/Layer/InstancesSpec.purs+93
@@ -2,11 +2,14 @@ module Test.Yoga.Om.Layer.InstancesSpec where
2
2
3import Prelude
3import Prelude
4
4
5+import Control.Alt ((<|>))
6+import Control.Monad.Reader (ask, local)
5import Control.Parallel (parApply)
7import Control.Parallel (parApply)
6import Effect.Class (liftEffect)
8import Effect.Class (liftEffect)
7import Effect.Ref as Ref
9import Effect.Ref as Ref
8import Test.Spec (Spec, describe, it)
10import Test.Spec (Spec, describe, it)
9import Test.Spec.Assertions (shouldEqual)
11import Test.Spec.Assertions (shouldEqual)
12+import Effect.Exception as Exception
10import Yoga.Om as Om
13import Yoga.Om as Om
11import Yoga.Om.Layer (OmLayer, makeLayer, runLayer)
14import Yoga.Om.Layer (OmLayer, makeLayer, runLayer)
12
15
@@ -46,6 +49,96 @@ spec = do
46 result <- Om.runOm {} { exception: \_ -> pure { result: 0 } } (runLayer {} layer)
49 result <- Om.runOm {} { exception: \_ -> pure { result: 0 } } (runLayer {} layer)
47 result.result `shouldEqual` 15
50 result.result `shouldEqual` 15
48
51
52+ it "runs layers against a superset context" do
53+ let
54+ layer :: OmLayer (a :: Int) () { out :: Int }
55+ layer = makeLayer do
56+ { a } <- Om.ask
57+ pure { out: a + 1 }
58+
59+ ctx = { a: 41, extra: "ok" }
60+
61+ result <- Om.runOm ctx { exception: \_ -> pure { out: 0 } } (runLayer ctx layer)
62+ result.out `shouldEqual` 42
63+
64+ describe "MonadEffect / MonadAff" do
65+
66+ it "liftEffect works directly in layer bind" do
67+ ref <- liftEffect $ Ref.new 0
68+ let
69+ layer :: OmLayer () () Int
70+ layer = do
71+ liftEffect $ Ref.write 42 ref
72+ liftEffect $ Ref.read ref
73+
74+ result <- Om.runOm {} { exception: \_ -> pure 0 } (runLayer {} layer)
75+ result `shouldEqual` 42
76+
77+ describe "MonadAsk / MonadReader" do
78+
79+ it "ask reads the context directly" do
80+ let
81+ layer :: OmLayer (port :: Int) () Int
82+ layer = do
83+ ctx <- ask
84+ pure ctx.port
85+
86+ result <- Om.runOm { port: 8080 } { exception: \_ -> pure 0 } (runLayer { port: 8080 } layer)
87+ result `shouldEqual` 8080
88+
89+ it "local modifies context for a layer" do
90+ let
91+ inner :: OmLayer (port :: Int) () Int
92+ inner = do
93+ ctx <- ask
94+ pure ctx.port
95+
96+ layer :: OmLayer (port :: Int) () Int
97+ layer = local (\r -> r { port = r.port + 1 }) inner
98+
99+ result <- Om.runOm { port: 8080 } { exception: \_ -> pure 0 } (runLayer { port: 8080 } layer)
100+ result `shouldEqual` 8081
101+
102+ describe "MonadThrow / MonadError" do
103+
104+ it "throwError raises into the error channel" do
105+ let
106+ layer :: OmLayer () (myErr :: String) Int
107+ layer = Om.throw { myErr: "boom" }
108+
109+ result <- Om.runOm {}
110+ { exception: \_ -> pure (-1), myErr: \_ -> pure (-2) }
111+ (runLayer {} layer)
112+ result `shouldEqual` (-2)
113+
114+ describe "Alt" do
115+
116+ it "falls back to second layer on failure" do
117+ let
118+ failing :: OmLayer () () Int
119+ failing = Om.throw { exception: Exception.error "nope" }
120+
121+ fallback :: OmLayer () () Int
122+ fallback = pure 99
123+
124+ layer = failing <|> fallback
125+
126+ result <- Om.runOm {} { exception: \_ -> pure (-1) } (runLayer {} layer)
127+ result `shouldEqual` 99
128+
129+ describe "Semigroup" do
130+
131+ it "appends layer outputs" do
132+ let
133+ a :: OmLayer () () String
134+ a = pure "hello"
135+
136+ b :: OmLayer () () String
137+ b = pure " world"
138+
139+ result <- Om.runOm {} { exception: \_ -> pure "" } (runLayer {} (a <> b))
140+ result `shouldEqual` "hello world"
141+
49 describe "Parallel" do
142 describe "Parallel" do
50
143
51 it "runs layers in parallel via parApply" do
144 it "runs layers in parallel via parApply" do
test/Test/Yoga/Om/Layer/PureInferenceSpec.pursnew file+22
@@ -0,0 +1,22 @@
1+module Test.Yoga.Om.Layer.PureInferenceSpec where
2+
3+import Prelude
4+
5+import Test.Spec (Spec, describe, it)
6+import Test.Spec.Assertions (shouldEqual)
7+import Yoga.Om as Om
8+import Yoga.Om.Layer (OmLayer, withValue, makeLayer, runLayer)
9+
10+spec :: Spec Unit
11+spec = describe "withValue" do
12+
13+ it "combines a plain record with a layer without needing VTA" do
14+ let
15+ layer :: OmLayer () () { value :: Int }
16+ layer = makeLayer (pure { value: 42 })
17+
18+ result = layer # withValue { extra: "hello" }
19+
20+ out <- Om.runOm {} { exception: \_ -> pure { value: -1, extra: "" } } (runLayer {} result)
21+ out.value `shouldEqual` 42
22+ out.extra `shouldEqual` "hello"