git.iko.soy/rowtype-yoga/purescript-yoga-om-layer
on main help sign in
9 files changed+325−644
since splitsnapshots
unifiedsplit
.psc-ide-port+1−1 .spec-results+96−33 README.md+1 compile-fail-tests/RunLayerOpenRowMissingDep.purs+19 spago.lock+17−600 spago.yaml+6−7 src/Yoga/Om/Layer.purs+70−3 test/Test/Yoga/Om/Layer/InstancesSpec.purs+93 test/Test/Yoga/Om/Layer/PureInferenceSpec.purs+22
.psc-ide-port+1−1
@@ -1 +1 @@
1−15887
1+15500
.spec-results+96−33
@@ -1,232 +1,295 @@
11[
2+ [
3+ "Alt falls back to second layer on failure",
4+ {
5+ "timestamp": "1775221839085.0",
6+ "success": true
7+ }
8+ ],
29 [
310 "Automatic Memoization builds the same layer only once when used in two branches",
411 {
5− "timestamp": "1771460758484.0",
12+ "timestamp": "1775221839085.0",
613 "success": true
714 }
815 ],
916 [
1017 "Automatic Memoization memoized bracketLayer acquires and releases only once",
1118 {
12− "timestamp": "1771460758484.0",
19+ "timestamp": "1775221839085.0",
1320 "success": true
1421 }
1522 ],
1623 [
1724 "Automatic Memoization memoized scoped layer runs finalizer only once",
1825 {
19− "timestamp": "1771460758484.0",
26+ "timestamp": "1775221839085.0",
2027 "success": true
2128 }
2229 ],
2330 [
2431 "Composition proves Row.Nub deduplicates shared requirements at type level",
2532 {
26− "timestamp": "1771460758484.0",
33+ "timestamp": "1775221839085.0",
2734 "success": true
2835 }
2936 ],
3037 [
3138 "Composition proves deduplication at runtime via shared Ref",
3239 {
33− "timestamp": "1771460758484.0",
40+ "timestamp": "1775221839085.0",
3441 "success": true
3542 }
3643 ],
3744 [
3845 "Error Channel composes layers with different error types",
3946 {
40− "timestamp": "1771460758484.0",
47+ "timestamp": "1775221839085.0",
4148 "success": true
4249 }
4350 ],
4451 [
4552 "Error Channel propagates errors from the first layer",
4653 {
47− "timestamp": "1771460758484.0",
54+ "timestamp": "1775221839085.0",
4855 "success": true
4956 }
5057 ],
5158 [
5259 "Error Channel propagates errors from the second layer",
5360 {
54− "timestamp": "1771460758484.0",
61+ "timestamp": "1775221839085.0",
5562 "success": true
5663 }
5764 ],
5865 [
5966 "Error Channel propagates errors through vertical composition",
6067 {
61− "timestamp": "1771460758484.0",
68+ "timestamp": "1775221839085.0",
6269 "success": true
6370 }
6471 ],
6572 [
6673 "Error Channel scoped layer error triggers finalizers",
6774 {
68− "timestamp": "1771460758484.0",
75+ "timestamp": "1775221839085.0",
6976 "success": true
7077 }
7178 ],
7279 [
7380 "Error Channel scoped layers with errors run via runScopedWith",
7481 {
75− "timestamp": "1771460758484.0",
82+ "timestamp": "1775221839085.0",
7683 "success": true
7784 }
7885 ],
7986 [
8087 "Functor / Apply / Bind applies two layers",
8188 {
82− "timestamp": "1771460758484.0",
89+ "timestamp": "1775221839085.0",
8390 "success": true
8491 }
8592 ],
8693 [
8794 "Functor / Apply / Bind binds layers sequentially",
8895 {
89− "timestamp": "1771460758484.0",
96+ "timestamp": "1775221839085.0",
9097 "success": true
9198 }
9299 ],
93100 [
94101 "Functor / Apply / Bind maps over layer output",
95102 {
96− "timestamp": "1771460758484.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",
97111 "success": true
98112 }
99113 ],
100114 [
101115 "Layer recovering does not retry on non-matching errors",
102116 {
103− "timestamp": "1771460758484.0",
117+ "timestamp": "1775221839085.0",
104118 "success": true
105119 }
106120 ],
107121 [
108122 "Layer recovering only retries the specified error in a multi-error layer",
109123 {
110− "timestamp": "1771460758484.0",
124+ "timestamp": "1775221839085.0",
111125 "success": true
112126 }
113127 ],
114128 [
115129 "Layer recovering preserves the error when retries are exhausted",
116130 {
117− "timestamp": "1771460758484.0",
131+ "timestamp": "1775221839085.0",
118132 "success": true
119133 }
120134 ],
121135 [
122136 "Layer recovering retries layer construction on matching errors",
123137 {
124− "timestamp": "1771460758484.0",
138+ "timestamp": "1775221839085.0",
125139 "success": true
126140 }
127141 ],
128142 [
129143 "Layer recovering uses RetryStatus in the check",
130144 {
131− "timestamp": "1771460758484.0",
145+ "timestamp": "1775221839085.0",
132146 "success": true
133147 }
134148 ],
135149 [
136150 "Layer repeating repeats layer construction while condition holds",
137151 {
138− "timestamp": "1771460758484.0",
152+ "timestamp": "1775221839085.0",
139153 "success": true
140154 }
141155 ],
142156 [
143157 "Layer repeating stops when policy exhausted",
144158 {
145− "timestamp": "1771460758484.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",
146188 "success": true
147189 }
148190 ],
149191 [
150192 "Parallel runs layers in parallel via parApply",
151193 {
152− "timestamp": "1771460758484.0",
194+ "timestamp": "1775221839085.0",
153195 "success": true
154196 }
155197 ],
156198 [
157199 "Scoped Layers combines scoped and non-scoped layers",
158200 {
159− "timestamp": "1771460758484.0",
201+ "timestamp": "1775221839085.0",
160202 "success": true
161203 }
162204 ],
163205 [
164206 "Scoped Layers fresh layer builds twice even when same base layer",
165207 {
166− "timestamp": "1771460758484.0",
208+ "timestamp": "1775221839085.0",
167209 "success": true
168210 }
169211 ],
170212 [
171213 "Scoped Layers runs finalizers in reverse order on success",
172214 {
173− "timestamp": "1771460758484.0",
215+ "timestamp": "1775221839085.0",
174216 "success": true
175217 }
176218 ],
177219 [
178220 "Scoped Layers runs finalizers when the callback throws",
179221 {
180− "timestamp": "1771460758484.0",
222+ "timestamp": "1775221839085.0",
181223 "success": true
182224 }
183225 ],
184226 [
185227 "Scoped Layers threads finalizers through vertical composition (provide)",
186228 {
187− "timestamp": "1771460758484.0",
229+ "timestamp": "1775221839085.0",
188230 "success": true
189231 }
190232 ],
191233 [
192234 "Scoped Layers works with bracketLayer",
193235 {
194− "timestamp": "1771460758484.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",
195251 "success": true
196252 }
197253 ],
198254 [
199255 "wireLayers memoizes shared dependencies within wireLayers",
200256 {
201− "timestamp": "1771460758484.0",
257+ "timestamp": "1775221839085.0",
202258 "success": true
203259 }
204260 ],
205261 [
206262 "wireLayers wireLayersDebug emits dependency graph as compiler warning",
207263 {
208− "timestamp": "1771460758484.0",
264+ "timestamp": "1775221839085.0",
209265 "success": true
210266 }
211267 ],
212268 [
213269 "wireLayers wires a diamond dependency",
214270 {
215− "timestamp": "1771460758484.0",
271+ "timestamp": "1775221839085.0",
216272 "success": true
217273 }
218274 ],
219275 [
220276 "wireLayers wires a simple linear chain (A -> B -> C)",
221277 {
222− "timestamp": "1771460758484.0",
278+ "timestamp": "1775221839085.0",
223279 "success": true
224280 }
225281 ],
226282 [
227283 "wireLayers wires independent layers with no inter-dependencies",
228284 {
229− "timestamp": "1771460758484.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",
230293 "success": true
231294 }
232295 ]
README.md+1
@@ -240,3 +240,4 @@ combineRequirements (OmLayer build1) (OmLayer build2) = makeLayer do
240240
241241**Status**: Proof of concept complete ✓ with working runtime implementation
242242**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
spago.lock+17−600
@@ -6,7 +6,7 @@
66 "core": {
77 "dependencies": [
88 {
9− "aff": ">=7.0.0 <9.0.0"
9+ "aff": ">=8.0.0 <9.0.0"
1010 },
1111 {
1212 "aff-retry": ">=2.0.0 <3.0.0"
@@ -14,6 +14,9 @@
1414 {
1515 "arrays": ">=7.3.0 <8.0.0"
1616 },
17+ {
18+ "control": ">=6.0.0 <7.0.0"
19+ },
1720 {
1821 "effect": ">=4.0.0 <5.0.0"
1922 },
@@ -47,6 +50,9 @@
4750 {
4851 "refs": ">=6.0.0 <7.0.0"
4952 },
53+ {
54+ "tailrec": ">=6.1.0 <7.0.0"
55+ },
5056 {
5157 "transformers": ">=6.1.0 <7.0.0"
5258 },
@@ -63,7 +69,7 @@
6369 "variant": ">=8.0.0 <9.0.0"
6470 },
6571 {
66− "yoga-om": ">=1.4.0 <2.0.0"
72+ "yoga-om": ">=2.0.0 <3.0.0"
6773 }
6874 ]
6975 },
@@ -88,598 +94,6 @@
8894 }
8995 }
9096 },
91− "package_set": {
92− "address": {
93− "registry": "73.2.0"
94− },
95− "compiler": ">=0.15.15 <0.16.0",
96− "content": {
97− "abc-parser": "2.1.0",
98− "ace": "9.1.0",
99− "address-rfc2821": "0.1.1",
100− "aff": "8.0.0",
101− "aff-bus": "6.0.0",
102− "aff-coroutines": "9.0.0",
103− "aff-promise": "4.0.0",
104− "aff-retry": "2.0.0",
105− "affjax": "13.0.0",
106− "affjax-node": "1.0.0",
107− "affjax-web": "1.0.0",
108− "ansi": "7.0.0",
109− "apexcharts": "0.5.0",
110− "applicative-phases": "1.0.0",
111− "argonaut": "9.0.0",
112− "argonaut-aeson-generic": "0.4.1",
113− "argonaut-codecs": "9.1.0",
114− "argonaut-core": "7.0.0",
115− "argonaut-generic": "8.0.0",
116− "argonaut-traversals": "10.0.0",
117− "argparse-basic": "2.0.0",
118− "array-builder": "0.1.2",
119− "array-search": "0.6.0",
120− "arraybuffer": "13.2.0",
121− "arraybuffer-builder": "3.1.0",
122− "arraybuffer-types": "3.0.2",
123− "arrays": "7.3.0",
124− "arrays-extra": "0.6.1",
125− "arrays-zipper": "2.0.1",
126− "ask": "1.0.0",
127− "assert": "6.0.0",
128− "assert-multiple": "0.4.0",
129− "avar": "5.0.1",
130− "axon": "0.0.3",
131− "b64": "0.0.8",
132− "barbies": "1.0.1",
133− "barlow-lens": "0.9.0",
134− "benchlib": "0.0.4",
135− "bifunctors": "6.1.0",
136− "bigints": "7.0.1",
137− "blessed": "1.0.0",
138− "bolson": "0.3.9",
139− "bookhound": "0.1.7",
140− "bower-json": "3.0.0",
141− "call-by-name": "4.0.1",
142− "canvas": "6.0.0",
143− "canvas-action": "9.0.0",
144− "cartesian": "1.0.6",
145− "catenable-lists": "7.0.0",
146− "cbor-stream": "1.3.0",
147− "chameleon": "1.0.0",
148− "chameleon-halogen": "1.0.3",
149− "chameleon-react-basic": "1.1.0",
150− "chameleon-styled": "2.5.0",
151− "chameleon-transformers": "1.0.0",
152− "channel": "1.0.0",
153− "checked-exceptions": "3.1.1",
154− "choku": "1.0.2",
155− "classless": "0.1.1",
156− "classless-arbitrary": "0.1.1",
157− "classless-decode-json": "0.1.1",
158− "classless-encode-json": "0.1.3",
159− "classnames": "2.0.0",
160− "codec": "6.1.0",
161− "codec-argonaut": "10.0.0",
162− "codec-json": "2.0.0",
163− "colors": "7.0.1",
164− "concur-core": "0.5.0",
165− "concur-react": "0.5.0",
166− "concurrent-queues": "3.0.0",
167− "console": "6.1.0",
168− "const": "6.0.0",
169− "contravariant": "6.0.0",
170− "control": "6.0.0",
171− "convertable-options": "1.0.0",
172− "coroutines": "7.0.0",
173− "css": "6.0.0",
174− "css-class-name-extractor": "0.0.4",
175− "css-frameworks": "1.1.0",
176− "csv-stream": "2.3.0",
177− "data-mvc": "0.0.2",
178− "datetime": "6.1.0",
179− "datetime-parsing": "0.2.0",
180− "debounce": "0.1.0",
181− "debug": "6.0.2",
182− "decimals": "7.1.0",
183− "default-values": "1.0.1",
184− "deku": "0.9.23",
185− "deno": "0.0.5",
186− "dissect": "1.0.0",
187− "distributive": "6.0.0",
188− "dodo-printer": "2.2.3",
189− "dom-filereader": "7.0.0",
190− "dom-indexed": "13.0.0",
191− "dom-simple": "0.4.0",
192− "dotenv": "4.0.3",
193− "droplet": "0.6.0",
194− "dts": "1.0.0",
195− "dual-numbers": "1.0.3",
196− "dynamic-buffer": "3.0.1",
197− "echarts-simple": "0.0.1",
198− "effect": "4.0.0",
199− "either": "6.1.0",
200− "elmish": "0.13.0",
201− "elmish-enzyme": "0.1.1",
202− "elmish-hooks": "0.11.0",
203− "elmish-html": "0.10.0",
204− "elmish-testing-library": "0.3.2",
205− "elmish-time-machine": "0.4.2",
206− "email-validate": "7.0.0",
207− "encoding": "0.0.9",
208− "enums": "6.0.1",
209− "env-names": "0.4.0",
210− "environment": "2.0.0",
211− "error": "2.0.0",
212− "eta-conversion": "0.3.2",
213− "exceptions": "6.1.0",
214− "exists": "6.0.0",
215− "exitcodes": "4.0.0",
216− "expect-inferred": "3.0.0",
217− "express": "0.9.1",
218− "ezfetch": "1.1.1",
219− "fahrtwind": "2.0.0",
220− "fakerjs": "0.0.1",
221− "fallback": "0.1.0",
222− "fast-vect": "1.2.0",
223− "fetch": "4.1.0",
224− "fetch-argonaut": "1.0.1",
225− "fetch-core": "5.1.0",
226− "fetch-yoga-json": "1.1.0",
227− "ffi-simple": "0.5.1",
228− "fft": "0.3.0",
229− "fft-js": "0.1.0",
230− "filterable": "5.0.0",
231− "fix-functor": "0.1.0",
232− "fixed-points": "7.0.0",
233− "fixed-precision": "5.0.0",
234− "flame": "1.6.0",
235− "float32": "2.0.0",
236− "fmt": "0.2.1",
237− "foldable-traversable": "6.0.0",
238− "foldable-traversable-extra": "0.0.6",
239− "foreign": "7.0.0",
240− "foreign-object": "4.1.0",
241− "foreign-readwrite": "3.4.0",
242− "forgetmenot": "0.1.0",
243− "fork": "6.0.0",
244− "form-urlencoded": "7.0.0",
245− "formatters": "7.0.0",
246− "framer-motion": "2.0.1",
247− "free": "7.1.0",
248− "freeap": "7.0.0",
249− "freer-free": "0.0.1",
250− "freet": "7.0.0",
251− "functions": "6.0.0",
252− "functor1": "3.0.0",
253− "functors": "5.0.0",
254− "fuzzy": "0.4.0",
255− "gen": "4.0.0",
256− "generate-values": "1.0.1",
257− "generic-router": "0.0.1",
258− "geojson": "0.0.5",
259− "geometria": "2.2.0",
260− "gesso": "1.0.1",
261− "gojs": "0.1.1",
262− "golem-fetch": "0.1.0",
263− "grain": "3.0.0",
264− "grain-router": "3.0.0",
265− "grain-virtualized": "3.0.0",
266− "graphql-client": "10.1.1",
267− "graphs": "8.1.0",
268− "group": "4.1.1",
269− "halogen": "7.0.0",
270− "halogen-bootstrap5": "5.3.2",
271− "halogen-canvas": "1.0.0",
272− "halogen-css": "10.0.0",
273− "halogen-declarative-canvas": "0.0.8",
274− "halogen-echarts-simple": "0.0.4",
275− "halogen-formless": "4.0.3",
276− "halogen-helix": "1.1.0",
277− "halogen-hooks": "0.6.3",
278− "halogen-hooks-extra": "0.9.0",
279− "halogen-infinite-scroll": "1.1.0",
280− "halogen-store": "0.5.4",
281− "halogen-storybook": "2.0.0",
282− "halogen-subscriptions": "2.0.0",
283− "halogen-svg-elems": "8.0.0",
284− "halogen-typewriter": "1.0.4",
285− "halogen-use-trigger-hooks": "1.0.0",
286− "halogen-vdom": "8.0.0",
287− "halogen-vdom-string-renderer": "0.5.0",
288− "halogen-xterm": "2.0.0",
289− "heckin": "2.0.1",
290− "heterogeneous": "0.7.0",
291− "homogeneous": "0.4.0",
292− "http-methods": "6.0.0",
293− "httpurple": "4.0.0",
294− "huffman": "0.4.0",
295− "humdrum": "0.0.1",
296− "hylograph-canvas": "0.1.0",
297− "hylograph-d3-kernel": "0.1.0",
298− "hylograph-graph": "0.1.0",
299− "hylograph-layout": "0.1.0",
300− "hylograph-music": "0.1.0",
301− "hylograph-optics": "0.1.0",
302− "hylograph-selection": "0.1.1",
303− "hylograph-simulation": "0.1.0",
304− "hylograph-simulation-core": "0.1.0",
305− "hylograph-simulation-halogen": "0.1.0",
306− "hylograph-transitions": "0.1.0",
307− "hylograph-wasm-kernel": "0.1.0",
308− "hyrule": "2.3.8",
309− "identity": "6.0.0",
310− "identy": "4.0.1",
311− "indexed-db": "1.0.0",
312− "indexed-monad": "3.0.0",
313− "ink": "0.0.1",
314− "int64": "3.0.0",
315− "integers": "6.0.0",
316− "interpolate": "5.0.2",
317− "intersection-observer": "1.0.1",
318− "invariant": "6.0.0",
319− "jarilo": "1.0.1",
320− "jelly": "0.10.0",
321− "jelly-router": "0.3.0",
322− "jelly-signal": "0.4.0",
323− "jest": "1.0.0",
324− "js-abort-controller": "1.0.0",
325− "js-bigints": "2.2.1",
326− "js-date": "8.0.0",
327− "js-fetch": "0.2.1",
328− "js-fileio": "3.0.0",
329− "js-intl": "1.1.4",
330− "js-iterators": "0.1.1",
331− "js-maps": "0.1.2",
332− "js-promise": "1.0.0",
333− "js-promise-aff": "1.0.0",
334− "js-timers": "6.1.0",
335− "js-uri": "3.1.0",
336− "jsdom": "1.0.0",
337− "json": "1.1.0",
338− "json-codecs": "5.0.0",
339− "justifill": "0.5.0",
340− "jwt": "0.0.9",
341− "labeled-data": "0.2.0",
342− "language-cst-parser": "0.14.1",
343− "lazy": "6.0.0",
344− "lazy-joe": "1.0.0",
345− "lcg": "4.0.0",
346− "leibniz": "5.0.0",
347− "leveldb": "1.0.1",
348− "liminal": "1.0.1",
349− "linalg": "6.0.0",
350− "lists": "7.0.0",
351− "literals": "1.0.2",
352− "logging": "3.0.0",
353− "logging-journald": "0.4.0",
354− "lumi-components": "18.0.0",
355− "machines": "7.0.0",
356− "maps-eager": "0.5.0",
357− "marionette": "1.0.0",
358− "marionette-react-basic-hooks": "0.1.1",
359− "marked": "0.1.0",
360− "matrices": "5.0.1",
361− "matryoshka": "1.0.0",
362− "maybe": "6.0.0",
363− "media-types": "6.0.0",
364− "meowclient": "1.0.0",
365− "midi": "4.0.0",
366− "milkis": "9.0.0",
367− "mimetype": "0.0.1",
368− "minibench": "4.0.1",
369− "mmorph": "7.0.0",
370− "monad-control": "5.0.0",
371− "monad-logger": "1.3.1",
372− "monad-loops": "0.5.0",
373− "monad-unlift": "1.0.1",
374− "monoid-extras": "0.0.1",
375− "monoidal": "0.16.0",
376− "morello": "0.4.0",
377− "mote": "3.0.0",
378− "motsunabe": "2.0.0",
379− "mvc": "0.0.1",
380− "mysql": "6.0.1",
381− "n3": "0.1.0",
382− "nano-id": "1.1.0",
383− "nanoid": "0.1.0",
384− "naturals": "3.0.0",
385− "nested-functor": "0.2.1",
386− "newtype": "5.0.0",
387− "nextjs": "0.1.1",
388− "nextui": "0.2.0",
389− "node-buffer": "9.0.0",
390− "node-child-process": "11.1.0",
391− "node-event-emitter": "3.0.0",
392− "node-execa": "5.0.0",
393− "node-fs": "9.2.0",
394− "node-glob-basic": "2.0.0",
395− "node-http": "9.1.0",
396− "node-http2": "1.1.1",
397− "node-human-signals": "1.0.0",
398− "node-net": "5.1.0",
399− "node-os": "5.1.0",
400− "node-path": "5.0.1",
401− "node-process": "11.2.0",
402− "node-readline": "8.1.1",
403− "node-sqlite3": "8.0.0",
404− "node-stream-pipes": "2.1.6",
405− "node-streams": "9.0.1",
406− "node-tls": "0.3.1",
407− "node-url": "7.0.1",
408− "node-workerbees": "0.3.1",
409− "node-zlib": "0.4.0",
410− "nonempty": "7.0.0",
411− "now": "6.0.0",
412− "npm-package-json": "2.0.0",
413− "nullable": "6.0.0",
414− "numberfield": "0.2.2",
415− "numbers": "9.0.1",
416− "oak": "3.1.1",
417− "oak-debug": "1.2.2",
418− "object-maps": "0.3.0",
419− "ocarina": "1.5.4",
420− "oooooooooorrrrrrrmm-lib": "0.0.1",
421− "open-colors-scales-and-schemes": "1.0.0",
422− "open-drawing": "6.0.4",
423− "open-folds": "6.4.0",
424− "open-foreign-generic": "11.0.3",
425− "open-memoize": "6.2.0",
426− "open-mkdirp-aff": "1.2.0",
427− "open-pairing": "6.2.0",
428− "open-smolder": "12.0.2",
429− "options": "7.0.0",
430− "optparse": "5.0.1",
431− "ordered-collections": "3.2.0",
432− "ordered-set": "0.5.0",
433− "orders": "6.0.0",
434− "org-doc": "0.1.0",
435− "owoify": "1.2.0",
436− "pairs": "9.0.1",
437− "parallel": "7.0.0",
438− "parsing": "11.0.0",
439− "parsing-dataview": "3.2.4",
440− "partial": "4.0.0",
441− "pathy": "9.0.0",
442− "pha": "0.13.0",
443− "phaser": "0.7.0",
444− "phylio": "1.1.2",
445− "pipes": "8.0.0",
446− "pirates-charm": "0.0.1",
447− "play": "1.0.0",
448− "pmock": "0.9.0",
449− "point-free": "1.0.0",
450− "pointed-list": "0.5.1",
451− "polymorphic-vectors": "4.0.0",
452− "posix-types": "6.0.0",
453− "postgresql": "2.0.21",
454− "precise": "6.0.0",
455− "precise-datetime": "7.0.0",
456− "prelude": "6.0.2",
457− "prettier-printer": "3.0.0",
458− "printf": "0.1.0",
459− "priority-queue": "0.1.2",
460− "profunctor": "6.0.1",
461− "profunctor-lenses": "8.0.0",
462− "protobuf": "4.4.0",
463− "psa-utils": "8.0.0",
464− "psci-support": "6.0.0",
465− "punycode": "1.0.0",
466− "pursfmt": "0.17.0",
467− "qualified-do": "2.2.0",
468− "quantities": "12.2.0",
469− "quickcheck": "8.0.1",
470− "quickcheck-combinators": "0.1.3",
471− "quickcheck-laws": "7.0.0",
472− "quickcheck-utf8": "0.0.0",
473− "random": "6.0.0",
474− "rationals": "6.0.0",
475− "rdf": "0.1.0",
476− "react": "11.0.0",
477− "react-aria": "0.2.0",
478− "react-basic": "17.0.0",
479− "react-basic-classic": "3.0.0",
480− "react-basic-dnd": "10.1.0",
481− "react-basic-dom": "7.0.0",
482− "react-basic-dom-beta": "0.1.1",
483− "react-basic-emotion": "7.1.0",
484− "react-basic-hooks": "9.0.0",
485− "react-basic-storybook": "2.0.0",
486− "react-dom": "8.0.0",
487− "react-halo": "3.0.0",
488− "react-icons": "1.1.5",
489− "react-markdown": "0.1.0",
490− "react-testing-library": "4.0.1",
491− "react-virtuoso": "1.0.0",
492− "reactix": "0.6.1",
493− "read": "1.0.1",
494− "recharts": "1.1.0",
495− "record": "4.0.0",
496− "record-extra": "5.0.1",
497− "record-extra-srghma": "0.2.8",
498− "record-ptional-fields": "0.1.2",
499− "record-studio": "1.0.4",
500− "refs": "6.0.0",
501− "remotedata": "5.0.1",
502− "repr": "0.5.0",
503− "resize-arrays": "0.0.1",
504− "resize-observer": "1.0.0",
505− "resource": "2.0.1",
506− "resourcet": "1.0.0",
507− "result": "1.0.3",
508− "return": "0.2.0",
509− "ring-modules": "5.0.1",
510− "rito": "0.3.4",
511− "roman": "0.4.0",
512− "rough-notation": "1.0.2",
513− "routing": "11.0.0",
514− "routing-duplex": "0.7.0",
515− "run": "5.0.0",
516− "safe-coerce": "2.0.0",
517− "safely": "4.0.1",
518− "school-of-music": "1.3.0",
519− "selection-foldable": "0.2.0",
520− "selective-functors": "1.0.1",
521− "semirings": "7.0.0",
522− "shuffle": "2.0.0",
523− "sigil": "0.2.0",
524− "signal": "13.0.0",
525− "simple-emitter": "3.0.1",
526− "simple-i18n": "2.0.1",
527− "simple-json": "9.0.0",
528− "simple-json-generics": "0.2.1",
529− "simple-ulid": "3.0.0",
530− "sized-matrices": "1.0.0",
531− "sized-vectors": "5.0.2",
532− "slug": "3.2.0",
533− "small-ffi": "4.0.1",
534− "soundfonts": "4.1.0",
535− "sparse-matrices": "2.0.1",
536− "sparse-polynomials": "3.0.1",
537− "spec": "8.1.1",
538− "spec-discovery": "8.4.1",
539− "spec-mocha": "5.1.1",
540− "spec-node": "0.0.3",
541− "spec-quickcheck": "5.0.2",
542− "spec-reporter-xunit": "0.7.1",
543− "splitmix": "2.1.0",
544− "ssrs": "1.0.0",
545− "st": "6.2.0",
546− "statistics": "0.3.2",
547− "strictlypositiveint": "1.0.1",
548− "string-parsers": "8.0.0",
549− "strings": "6.0.1",
550− "strings-extra": "4.0.0",
551− "stringutils": "0.0.12",
552− "structured-logging": "1.0.0",
553− "substitute": "0.2.3",
554− "supply": "0.2.0",
555− "svg-parser": "3.0.0",
556− "systemd-journald": "0.3.0",
557− "tagged": "4.0.2",
558− "tailrec": "6.1.0",
559− "tanstack-query": "2.0.0",
560− "tecton": "0.2.1",
561− "tecton-halogen": "0.2.0",
562− "test-unit": "17.0.0",
563− "text-formatting": "0.1.0",
564− "thermite": "6.3.1",
565− "thermite-dom": "0.3.1",
566− "these": "6.0.0",
567− "threading": "0.0.3",
568− "tidy": "0.11.1",
569− "tidy-codegen": "4.0.1",
570− "tldr": "0.0.0",
571− "toestand": "0.9.0",
572− "transformation-matrix": "1.0.1",
573− "transformers": "6.1.0",
574− "transit": "1.0.0",
575− "tree-rose": "4.0.2",
576− "trivial-unfold": "0.5.0",
577− "ts-bridge": "4.0.0",
578− "tuples": "7.0.0",
579− "two-or-more": "1.0.0",
580− "type-equality": "4.0.1",
581− "typedenv": "2.0.1",
582− "typelevel": "6.0.0",
583− "typelevel-lists": "2.1.0",
584− "typelevel-peano": "1.0.1",
585− "typelevel-prelude": "7.0.0",
586− "typelevel-regex": "0.0.3",
587− "typelevel-rows": "0.1.0",
588− "typisch": "0.4.0",
589− "uint": "7.0.0",
590− "ulid": "3.0.1",
591− "uncurried-transformers": "1.1.0",
592− "undefined": "2.0.0",
593− "undefined-is-not-a-problem": "1.1.0",
594− "unfoldable": "6.0.0",
595− "unicode": "6.0.0",
596− "unique": "0.6.1",
597− "unlift": "1.0.1",
598− "unordered-collections": "3.1.0",
599− "unsafe-coerce": "6.0.0",
600− "unsafe-reference": "5.0.0",
601− "untagged-to-tagged": "0.1.4",
602− "untagged-union": "1.0.0",
603− "uri": "9.0.0",
604− "url-immutable": "1.0.0",
605− "url-regex-safe": "0.1.1",
606− "uuid": "9.0.0",
607− "uuidv4": "1.0.0",
608− "validation": "6.0.0",
609− "variant": "8.0.0",
610− "variant-encodings": "2.0.0",
611− "variant-gen": "1.0.0",
612− "vectorfield": "1.0.1",
613− "vectors": "2.1.0",
614− "versions": "7.0.0",
615− "visx": "0.0.2",
616− "vitest": "2.0.0",
617− "web-clipboard": "6.0.0",
618− "web-cssom": "2.0.0",
619− "web-cssom-view": "0.1.0",
620− "web-dom": "6.0.0",
621− "web-dom-parser": "8.0.0",
622− "web-dom-xpath": "3.0.0",
623− "web-encoding": "3.0.0",
624− "web-events": "4.0.0",
625− "web-fetch": "4.0.1",
626− "web-file": "4.0.0",
627− "web-geometry": "0.1.0",
628− "web-html": "4.1.1",
629− "web-pointerevents": "2.0.0",
630− "web-proletarian": "1.0.0",
631− "web-promise": "3.2.0",
632− "web-resize-observer": "2.1.0",
633− "web-router": "1.0.0",
634− "web-socket": "4.0.0",
635− "web-storage": "5.0.0",
636− "web-streams": "4.0.0",
637− "web-touchevents": "4.0.0",
638− "web-uievents": "5.0.0",
639− "web-url": "2.0.0",
640− "web-workers": "1.1.0",
641− "web-xhr": "5.0.1",
642− "webb-aff-list": "0.0.2",
643− "webb-array": "0.0.2",
644− "webb-channel": "0.0.3",
645− "webb-commandline": "0.0.3",
646− "webb-directory": "0.0.7",
647− "webb-file": "0.0.1",
648− "webb-map": "0.0.1",
649− "webb-monad": "0.0.2",
650− "webb-mutex": "0.0.7",
651− "webb-parsing": "0.0.5",
652− "webb-random": "0.0.1",
653− "webb-refer": "0.0.3",
654− "webb-set": "0.0.1",
655− "webb-slot": "0.0.1",
656− "webb-stateful": "0.0.3",
657− "webb-string": "0.0.1",
658− "webb-test": "0.0.1",
659− "webb-thread": "0.0.2",
660− "webb-writer": "0.0.3",
661− "webextension-polyfill": "0.1.0",
662− "webgpu": "0.0.1",
663− "which": "2.0.0",
664− "whine-core": "0.0.33",
665− "xterm": "1.0.0",
666− "yaml-next": "3.1.1",
667− "yoga-fetch": "1.0.1",
668− "yoga-json": "5.1.0",
669− "yoga-om": "0.1.0",
670− "yoga-postgres": "6.0.0",
671− "yoga-react-dom": "1.0.1",
672− "yoga-redis": "0.1.0",
673− "yoga-sql-types": "0.1.0",
674− "yoga-subtlecrypto": "0.1.0",
675− "yoga-test-docker": "0.1.0",
676− "yoga-tree": "1.0.0",
677− "yoga-tree-svg": "0.1.0",
678− "yoga-tree-utils": "1.0.0",
679− "z3": "0.0.2",
680− "zipperarray": "2.0.0"
681− }
682− },
68397 "extra_packages": {}
68498 },
68599 "packages": {
@@ -1590,8 +1004,8 @@
15901004 },
15911005 "spec": {
15921006 "type": "registry",
1593− "version": "8.1.1",
1594− "integrity": "sha256-b6qjDb7lUJGLV+WEoywLu6BCXC4zGpfbN43mTL+vBeI=",
1007+ "version": "8.1.2",
1008+ "integrity": "sha256-klPPJ6lBQI1CGOAdLJ+ZoGRt8XZippkTKjn53BrSkL0=",
15951009 "dependencies": [
15961010 "aff",
15971011 "ansi",
@@ -1624,8 +1038,8 @@
16241038 },
16251039 "spec-discovery": {
16261040 "type": "registry",
1627− "version": "8.4.1",
1628− "integrity": "sha256-guvY+NtSOBQmM+HqNJ7woX6taN8hS1wEz7JDmdKF11E=",
1041+ "version": "8.4.0",
1042+ "integrity": "sha256-aFp8bllT/wrxQu8hMlDchQQY08dQAz+Xdq2hZi5z3yY=",
16291043 "dependencies": [
16301044 "aff",
16311045 "effect",
@@ -1817,10 +1231,11 @@
18171231 },
18181232 "yoga-om": {
18191233 "type": "registry",
1820− "version": "0.1.0",
1821− "integrity": "sha256-0EGWo/UOVLzZ14DPtdv0C+bTvNO49wNgtTBIXI6QHqU=",
1234+ "version": "2.0.0",
1235+ "integrity": "sha256-eenoujD2rJzR6gYJbbIiqLhXHC1C5dagu91HZkAEt/Y=",
18221236 "dependencies": [
18231237 "aff",
1238+ "aff-retry",
18241239 "avar",
18251240 "console",
18261241 "control",
@@ -1829,12 +1244,14 @@
18291244 "either",
18301245 "exceptions",
18311246 "functions",
1247+ "heterogeneous",
18321248 "maybe",
18331249 "newtype",
18341250 "parallel",
18351251 "prelude",
18361252 "record",
18371253 "record-studio",
1254+ "refs",
18381255 "tailrec",
18391256 "transformers",
18401257 "tuples",
spago.yaml+6−7
@@ -1,19 +1,17 @@
1−workspace:
2− packageSet:
3− registry: 73.2.0
4−
1+workspace: {}
52package:
63 name: yoga-om-layer
74 publish:
8− version: 1.2.1
5+ version: 2.1.0
96 license: MIT
107 location:
118 githubOwner: rowtype-yoga
129 githubRepo: purescript-yoga-om-layer
1310 dependencies:
14− - aff: ">=7.0.0 <9.0.0"
11+ - aff: ">=8.0.0 <9.0.0"
1512 - aff-retry: ">=2.0.0 <3.0.0"
1613 - arrays: ">=7.3.0 <8.0.0"
14+ - control: ">=6.0.0 <7.0.0"
1715 - effect: ">=4.0.0 <5.0.0"
1816 - exceptions: ">=6.1.0 <7.0.0"
1917 - foldable-traversable: ">=6.0.0 <7.0.0"
@@ -25,12 +23,13 @@ package:
2523 - record: ">=4.0.0 <5.0.0"
2624 - record-studio: ">=1.0.4 <2.0.0"
2725 - refs: ">=6.0.0 <7.0.0"
26+ - tailrec: ">=6.1.0 <7.0.0"
2827 - transformers: ">=6.1.0 <7.0.0"
2928 - type-equality: ">=4.0.1 <5.0.0"
3029 - typelevel-prelude: ">=7.0.0 <8.0.0"
3130 - unsafe-coerce: ">=6.0.0 <7.0.0"
3231 - variant: ">=8.0.0 <9.0.0"
33− - yoga-om: ">=1.4.0 <2.0.0"
32+ - yoga-om: ">=2.0.0 <3.0.0"
3433 test:
3534 main: Test.Main
3635 dependencies:
src/Yoga/Om/Layer.purs+70−3
@@ -15,6 +15,7 @@ module Yoga.Om.Layer
1515 , withScopedWith
1616 , scoped
1717 , combineRequirements
18+ , withValue
1819 , provide
1920 , (>->)
2021 , recovering
@@ -52,10 +53,19 @@ module Yoga.Om.Layer
5253
5354import Prelude
5455
55−import Control.Monad.Error.Class (catchError, throwError)
56−import Data.Symbol (class IsSymbol)
56+import Control.Alt (class Alt)
57+import Control.Alt as Alt
58+import Control.Alternative (class Alternative)
59+import Control.Monad.Error.Class (class MonadError, class MonadThrow, catchError, throwError)
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)
5763import Control.Parallel (class Parallel)
5864import 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)
5969import Data.Array as Array
6070import Data.Foldable (for_)
6171import Data.Map (Map)
@@ -66,7 +76,10 @@ import Data.Variant (class VariantMatchCases, onMatch)
6676import Effect (Effect)
6777import Effect.Aff (Aff, bracket)
6878import Effect.Aff.Retry (RetryPolicyM, RetryStatus, applyAndDelay, defaultRetryStatus)
69−import Effect.Class (liftEffect)
79+import Effect.Aff.Class (class MonadAff)
80+import Effect.Aff.Class as Aff
81+import Effect.Class (class MonadEffect, liftEffect)
82+import Effect.Class (class MonadEffect, liftEffect) as Effect
7083import Effect.Exception (Error)
7184import Effect.Ref (Ref)
7285import Effect.Ref as Ref
@@ -86,6 +99,7 @@ import Type.Equality (class TypeEquals)
8699import Unsafe.Coerce (unsafeCoerce)
87100import Yoga.Om (Om, ParOm)
88101import Yoga.Om as Om
102+import Yoga.Om.Error (Exception)
89103
90104-- =============================================================================
91105-- Scope — a first-class representation of resource lifetime
@@ -174,6 +188,36 @@ instance Bind (OmLayer req err) where
174188
175189instance Monad (OmLayer req err)
176190
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+
177221instance Functor (ParOmLayer req err) where
178222 map f (ParOmLayer p) = ParOmLayer (map f p)
179223
@@ -183,6 +227,14 @@ instance Apply (ParOmLayer req err) where
183227instance Applicative (ParOmLayer req err) where
184228 pure a = ParOmLayer (pure a)
185229
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+
186238instance Parallel (ParOmLayer req err) (OmLayer req err) where
187239 parallel (OmLayer _ om) = ParOmLayer (Parallel.parallel om)
188240 sequential (ParOmLayer p) = OmLayer (freshId unit) (Parallel.sequential p)
@@ -340,9 +392,13 @@ else instance
340392-- =============================================================================
341393
342394-- | 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.
343398runLayer
344399 :: forall req err a available
345400 . CheckAllProvided req available
401+ => Keys req
346402 => Record available
347403 -> OmLayer req err a
348404 -> Om (Record available) err a
@@ -440,6 +496,17 @@ combineRequirements layer1 layer2 = OmLayer (freshId unit) do
440496 rec2 <- Om.expand (buildLayer layer2)
441497 pure (Record.merge rec1 rec2)
442498
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+
443510-- =============================================================================
444511-- Vertical composition — feed output of one layer into input of another
445512-- =============================================================================
test/Test/Yoga/Om/Layer/InstancesSpec.purs+93
@@ -2,11 +2,14 @@ module Test.Yoga.Om.Layer.InstancesSpec where
22
33import Prelude
44
5+import Control.Alt ((<|>))
6+import Control.Monad.Reader (ask, local)
57import Control.Parallel (parApply)
68import Effect.Class (liftEffect)
79import Effect.Ref as Ref
810import Test.Spec (Spec, describe, it)
911import Test.Spec.Assertions (shouldEqual)
12+import Effect.Exception as Exception
1013import Yoga.Om as Om
1114import Yoga.Om.Layer (OmLayer, makeLayer, runLayer)
1215
@@ -46,6 +49,96 @@ spec = do
4649 result <- Om.runOm {} { exception: \_ -> pure { result: 0 } } (runLayer {} layer)
4750 result.result `shouldEqual` 15
4851
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+
49142 describe "Parallel" do
50143
51144 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"