@@ -2,6 +2,7 @@ module Test.Main where
22
33import Prelude
44
5+import Control.Parallel (parApply)
56import Effect (Effect)
67import Effect.Aff (launchAff_, throwError, try)
78import Effect.Aff.Class (liftAff)
@@ -29,12 +30,12 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
2930
3031 it "proves Row.Nub deduplicates shared requirements at type level" do
3132 let
32− loggerLayer :: OmLayer (config :: Config) (logger :: Logger) ()
33+ loggerLayer :: OmLayer (config :: Config) () { logger :: Logger }
3334 loggerLayer = makeLayer do
3435 { config } <- Om.ask
3536 pure { logger: { log: \msg -> Console.log $ "[" <> config.host <> "] " <> msg } }
3637
37− databaseLayer :: OmLayer (config :: Config) (database :: Database) ()
38+ databaseLayer :: OmLayer (config :: Config) () { database :: Database }
3839 databaseLayer = makeLayer do
3940 { config } <- Om.ask
4041 pure { database: { query: \q -> pure [ "Result from " <> config.host ] } }
@@ -49,7 +50,7 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
4950 it "proves deduplication at runtime via shared Ref" do
5051 accessLog <- liftEffect $ Ref.new ""
5152 let
52− combinedLayer :: OmLayer (config :: Config, accessLog :: Ref.Ref String) (logger :: Logger, database :: Database) ()
53+ combinedLayer :: OmLayer (config :: Config, accessLog :: Ref.Ref String) () { logger :: Logger, database :: Database }
5354 combinedLayer = makeLayer do
5455 { config, accessLog: log } <- Om.ask
5556 liftEffect $ Ref.modify_ (_ <> "config-accessed-by-logger ") log
@@ -79,22 +80,22 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
7980 it "runs finalizers in reverse order on success" do
8081 log <- liftEffect $ Ref.new []
8182 let
82− layerA :: OmLayer (scope :: Scope) (a :: String) ()
83+ layerA :: OmLayer (scope :: Scope) () { a :: String }
8384 layerA = makeScopedLayer
8485 (pure { a: "A" })
8586 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "release-A" ]) log)
8687
87− layerB :: OmLayer (scope :: Scope) (b :: String) ()
88+ layerB :: OmLayer (scope :: Scope) () { b :: String }
8889 layerB = makeScopedLayer
8990 (pure { b: "B" })
9091 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "release-B" ]) log)
9192
92− layerC :: OmLayer (scope :: Scope) (c :: String) ()
93+ layerC :: OmLayer (scope :: Scope) () { c :: String }
9394 layerC = makeScopedLayer
9495 (pure { c: "C" })
9596 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "release-C" ]) log)
9697
97− layerBC :: OmLayer (scope :: Scope) (b :: String, c :: String) ()
98+ layerBC :: OmLayer (scope :: Scope) () { b :: String, c :: String }
9899 layerBC = combineRequirements layerB layerC
99100
100101 combined = combineRequirements layerA layerBC
@@ -106,7 +107,7 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
106107 it "runs finalizers when the callback throws" do
107108 log <- liftEffect $ Ref.new []
108109 let
109− layer :: OmLayer (scope :: Scope) (value :: String) ()
110+ layer :: OmLayer (scope :: Scope) () { value :: String }
110111 layer = makeScopedLayer
111112 (pure { value: "acquired" })
112113 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "released" ]) log)
@@ -119,12 +120,12 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
119120 it "threads finalizers through vertical composition (provide)" do
120121 log <- liftEffect $ Ref.new []
121122 let
122− baseLayer :: OmLayer (scope :: Scope) (base :: String) ()
123+ baseLayer :: OmLayer (scope :: Scope) () { base :: String }
123124 baseLayer = makeScopedLayer
124125 (pure { base: "base-value" })
125126 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "release-base" ]) log)
126127
127− upperLayer :: OmLayer (scope :: Scope, base :: String) (upper :: String) ()
128+ upperLayer :: OmLayer (scope :: Scope, base :: String) () { upper :: String }
128129 upperLayer = makeScopedLayer
129130 ( do
130131 { base } <- Om.ask
@@ -158,23 +159,23 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
158159 it "fresh layer builds twice even when same base layer" do
159160 counter <- liftEffect $ Ref.new 0
160161 let
161− baseLayer :: OmLayer (scope :: Scope) (value :: String) ()
162+ baseLayer :: OmLayer (scope :: Scope) () { value :: String }
162163 baseLayer = makeLayer do
163164 liftEffect $ Ref.modify_ (_ + 1) counter
164165 pure { value: "built" }
165166
166− branch1 :: OmLayer (scope :: Scope) (out1 :: String) ()
167+ branch1 :: OmLayer (scope :: Scope) () { out1 :: String }
167168 branch1 = consumer `provide` fresh baseLayer
168169 where
169− consumer :: OmLayer (scope :: Scope, value :: String) (out1 :: String) ()
170+ consumer :: OmLayer (scope :: Scope, value :: String) () { out1 :: String }
170171 consumer = makeLayer do
171172 { value } <- Om.ask
172173 pure { out1: value <> "-1" }
173174
174− branch2 :: OmLayer (scope :: Scope) (out2 :: String) ()
175+ branch2 :: OmLayer (scope :: Scope) () { out2 :: String }
175176 branch2 = consumer `provide` fresh baseLayer
176177 where
177− consumer :: OmLayer (scope :: Scope, value :: String) (out2 :: String) ()
178+ consumer :: OmLayer (scope :: Scope, value :: String) () { out2 :: String }
178179 consumer = makeLayer do
179180 { value } <- Om.ask
180181 pure { out2: value <> "-2" }
@@ -190,12 +191,12 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
190191 it "combines scoped and non-scoped layers" do
191192 log <- liftEffect $ Ref.new []
192193 let
193− scopedLayer :: OmLayer (scope :: Scope) (scoped :: String) ()
194+ scopedLayer :: OmLayer (scope :: Scope) () { scoped :: String }
194195 scopedLayer = makeScopedLayer
195196 (pure { scoped: "yes" })
196197 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "release-scoped" ]) log)
197198
198− plainLayer :: OmLayer () (plain :: String) ()
199+ plainLayer :: OmLayer () () { plain :: String }
199200 plainLayer = makeLayer (pure { plain: "no-finalizer" })
200201
201202 combined = combineRequirements scopedLayer plainLayer
@@ -211,23 +212,23 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
211212 it "builds the same layer only once when used in two branches" do
212213 counter <- liftEffect $ Ref.new 0
213214 let
214− expensiveLayer :: OmLayer (scope :: Scope) (value :: String) ()
215+ expensiveLayer :: OmLayer (scope :: Scope) () { value :: String }
215216 expensiveLayer = makeLayer do
216217 liftEffect $ Ref.modify_ (_ + 1) counter
217218 pure { value: "built" }
218219
219− branch1 :: OmLayer (scope :: Scope) (out1 :: String) ()
220+ branch1 :: OmLayer (scope :: Scope) () { out1 :: String }
220221 branch1 = consumer `provide` expensiveLayer
221222 where
222− consumer :: OmLayer (scope :: Scope, value :: String) (out1 :: String) ()
223+ consumer :: OmLayer (scope :: Scope, value :: String) () { out1 :: String }
223224 consumer = makeLayer do
224225 { value } <- Om.ask
225226 pure { out1: value <> "-1" }
226227
227− branch2 :: OmLayer (scope :: Scope) (out2 :: String) ()
228+ branch2 :: OmLayer (scope :: Scope) () { out2 :: String }
228229 branch2 = consumer `provide` expensiveLayer
229230 where
230− consumer :: OmLayer (scope :: Scope, value :: String) (out2 :: String) ()
231+ consumer :: OmLayer (scope :: Scope, value :: String) () { out2 :: String }
231232 consumer = makeLayer do
232233 { value } <- Om.ask
233234 pure { out2: value <> "-2" }
@@ -243,23 +244,23 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
243244 it "memoized scoped layer runs finalizer only once" do
244245 log <- liftEffect $ Ref.new []
245246 let
246− dbLayer :: OmLayer (scope :: Scope) (db :: String) ()
247+ dbLayer :: OmLayer (scope :: Scope) () { db :: String }
247248 dbLayer = makeScopedLayer
248249 (pure { db: "connected" })
249250 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "db-closed" ]) log)
250251
251− repoBranch :: OmLayer (scope :: Scope) (repo :: String) ()
252+ repoBranch :: OmLayer (scope :: Scope) () { repo :: String }
252253 repoBranch = repo `provide` dbLayer
253254 where
254− repo :: OmLayer (scope :: Scope, db :: String) (repo :: String) ()
255+ repo :: OmLayer (scope :: Scope, db :: String) () { repo :: String }
255256 repo = makeLayer do
256257 { db } <- Om.ask
257258 pure { repo: db <> "-repo" }
258259
259− analyticsBranch :: OmLayer (scope :: Scope) (analytics :: String) ()
260+ analyticsBranch :: OmLayer (scope :: Scope) () { analytics :: String }
260261 analyticsBranch = analytics `provide` dbLayer
261262 where
262− analytics :: OmLayer (scope :: Scope, db :: String) (analytics :: String) ()
263+ analytics :: OmLayer (scope :: Scope, db :: String) () { analytics :: String }
263264 analytics = makeLayer do
264265 { db } <- Om.ask
265266 pure { analytics: db <> "-analytics" }
@@ -275,7 +276,7 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
275276 it "memoized bracketLayer acquires and releases only once" do
276277 log <- liftEffect $ Ref.new []
277278 let
278− connLayer :: OmLayer (scope :: Scope) (conn :: Int) ()
279+ connLayer :: OmLayer (scope :: Scope) () { conn :: Int }
279280 connLayer = bracketLayer
280281 ( do
281282 liftEffect $ Ref.modify_ (_ <> [ "acquire" ]) log
@@ -284,18 +285,18 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
284285 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "release" ]) log)
285286 (\n -> pure { conn: n })
286287
287− branch1 :: OmLayer (scope :: Scope) (x :: String) ()
288+ branch1 :: OmLayer (scope :: Scope) () { x :: String }
288289 branch1 = upper `provide` connLayer
289290 where
290− upper :: OmLayer (scope :: Scope, conn :: Int) (x :: String) ()
291+ upper :: OmLayer (scope :: Scope, conn :: Int) () { x :: String }
291292 upper = makeLayer do
292293 { conn } <- Om.ask
293294 pure { x: show conn }
294295
295− branch2 :: OmLayer (scope :: Scope) (y :: String) ()
296+ branch2 :: OmLayer (scope :: Scope) () { y :: String }
296297 branch2 = upper `provide` connLayer
297298 where
298− upper :: OmLayer (scope :: Scope, conn :: Int) (y :: String) ()
299+ upper :: OmLayer (scope :: Scope, conn :: Int) () { y :: String }
299300 upper = makeLayer do
300301 { conn } <- Om.ask
301302 pure { y: show (conn + 1) }
@@ -312,15 +313,15 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
312313
313314 it "composes layers with different error types" do
314315 let
315− dbLayer :: OmLayer () (db :: String) (dbError :: String)
316+ dbLayer :: OmLayer () (dbError :: String) { db :: String }
316317 dbLayer = makeLayer do
317318 pure { db: "connected" }
318319
319− cacheLayer :: OmLayer () (cache :: String) (cacheError :: String)
320+ cacheLayer :: OmLayer () (cacheError :: String) { cache :: String }
320321 cacheLayer = makeLayer do
321322 pure { cache: "ready" }
322323
323− combined :: OmLayer () (db :: String, cache :: String) (dbError :: String, cacheError :: String)
324+ combined :: OmLayer () (dbError :: String, cacheError :: String) { db :: String, cache :: String }
324325 combined = combineRequirements dbLayer cacheLayer
325326
326327 result <- Om.runOm {}
@@ -334,11 +335,11 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
334335
335336 it "propagates errors from the first layer" do
336337 let
337− failingLayer :: OmLayer () (db :: String) (dbError :: String)
338+ failingLayer :: OmLayer () (dbError :: String) { db :: String }
338339 failingLayer = makeLayer do
339340 Om.throw { dbError: "connection refused" }
340341
341− okLayer :: OmLayer () (cache :: String) ()
342+ okLayer :: OmLayer () () { cache :: String }
342343 okLayer = makeLayer do
343344 pure { cache: "ready" }
344345
@@ -358,11 +359,11 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
358359
359360 it "propagates errors from the second layer" do
360361 let
361− okLayer :: OmLayer () (db :: String) ()
362+ okLayer :: OmLayer () () { db :: String }
362363 okLayer = makeLayer do
363364 pure { db: "connected" }
364365
365− failingLayer :: OmLayer () (cache :: String) (cacheError :: String)
366+ failingLayer :: OmLayer () (cacheError :: String) { cache :: String }
366367 failingLayer = makeLayer do
367368 Om.throw { cacheError: "cache unavailable" }
368369
@@ -382,11 +383,11 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
382383
383384 it "propagates errors through vertical composition" do
384385 let
385− baseLayer :: OmLayer () (base :: String) (baseError :: String)
386+ baseLayer :: OmLayer () (baseError :: String) { base :: String }
386387 baseLayer = makeLayer do
387388 Om.throw { baseError: "base failed" }
388389
389− upperLayer :: OmLayer (base :: String) (upper :: String) (upperError :: String)
390+ upperLayer :: OmLayer (base :: String) (upperError :: String) { upper :: String }
390391 upperLayer = makeLayer do
391392 { base } <- Om.ask
392393 pure { upper: base <> "-extended" }
@@ -409,12 +410,12 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
409410 it "scoped layers with errors run via runScopedWith" do
410411 log <- liftEffect $ Ref.new []
411412 let
412− dbLayer :: OmLayer (scope :: Scope) (db :: String) (dbError :: String)
413+ dbLayer :: OmLayer (scope :: Scope) (dbError :: String) { db :: String }
413414 dbLayer = makeScopedLayer
414415 (pure { db: "connected" })
415416 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "db-closed" ]) log)
416417
417− cacheLayer :: OmLayer (scope :: Scope) (cache :: String) (cacheError :: String)
418+ cacheLayer :: OmLayer (scope :: Scope) (cacheError :: String) { cache :: String }
418419 cacheLayer = makeScopedLayer
419420 (pure { cache: "ready" })
420421 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "cache-closed" ]) log)
@@ -436,12 +437,12 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
436437 it "scoped layer error triggers finalizers" do
437438 log <- liftEffect $ Ref.new []
438439 let
439− goodLayer :: OmLayer (scope :: Scope) (good :: String) ()
440+ goodLayer :: OmLayer (scope :: Scope) () { good :: String }
440441 goodLayer = makeScopedLayer
441442 (pure { good: "ok" })
442443 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "good-closed" ]) log)
443444
444− failLayer :: OmLayer (scope :: Scope) (bad :: String) (buildError :: String)
445+ failLayer :: OmLayer (scope :: Scope) (buildError :: String) { bad :: String }
445446 failLayer = makeScopedLayer
446447 (Om.throw { buildError: "build failed" })
447448 (\_ -> liftEffect $ Ref.modify_ (_ <> [ "bad-closed" ]) log)
@@ -459,3 +460,59 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
459460 Left _ -> pure unit
460461 finalLog <- liftEffect $ Ref.read log
461462 finalLog `shouldEqual` [ "good-closed" ]
463+
464+ describe "Functor / Apply / Bind" do
465+
466+ it "maps over layer output" do
467+ let
468+ layer :: OmLayer () () { value :: Int }
469+ layer = makeLayer (pure { value: 10 })
470+
471+ mapped = layer <#> \r -> { doubled: r.value * 2 }
472+
473+ result <- Om.runOm {} { exception: \_ -> pure { doubled: 0 } } (runLayer {} mapped)
474+ result.doubled `shouldEqual` 20
475+
476+ it "applies two layers" do
477+ let
478+ fLayer :: OmLayer () () ({ a :: Int } -> { a :: Int, b :: String })
479+ fLayer = makeLayer (pure \r -> { a: r.a, b: show r.a })
480+
481+ aLayer :: OmLayer () () { a :: Int }
482+ aLayer = makeLayer (pure { a: 42 })
483+
484+ result <- Om.runOm {} { exception: \_ -> pure { a: 0, b: "" } } (runLayer {} (fLayer <*> aLayer))
485+ result.a `shouldEqual` 42
486+ result.b `shouldEqual` "42"
487+
488+ it "binds layers sequentially" do
489+ let
490+ layer = do
491+ r <- makeLayer (pure { x: 5 }) :: OmLayer () () { x :: Int }
492+ makeLayer (pure { result: r.x + 10 })
493+
494+ result <- Om.runOm {} { exception: \_ -> pure { result: 0 } } (runLayer {} layer)
495+ result.result `shouldEqual` 15
496+
497+ describe "Parallel" do
498+
499+ it "runs layers in parallel via parApply" do
500+ log <- liftEffect $ Ref.new []
501+ let
502+ layerA :: OmLayer () () { a :: String }
503+ layerA = makeLayer do
504+ liftEffect $ Ref.modify_ (_ <> [ "a" ]) log
505+ pure { a: "A" }
506+
507+ layerB :: OmLayer () () { b :: String }
508+ layerB = makeLayer do
509+ liftEffect $ Ref.modify_ (_ <> [ "b" ]) log
510+ pure { b: "B" }
511+
512+ combined = parApply (map (\ra rb -> { a: ra.a, b: rb.b }) layerA) layerB
513+
514+ result <- Om.runOm {} { exception: \_ -> pure { a: "", b: "" } } (runLayer {} combined)
515+ result.a `shouldEqual` "A"
516+ result.b `shouldEqual` "B"
517+ finalLog <- liftEffect $ Ref.read log
518+ finalLog `shouldEqual` [ "a", "b" ]