git.iko.soy/rowtype-yoga/purescript-yoga-om-layer
on main help sign in
Add fresh combinator test
commitd9eba3685609edb242207762a55a6f60c0a9806b authorMark <mark.eibes@gmail.com> dateFri 13 Feb 2026, 22:10 (8 months ago) parentdd4fb21
2 files changed+112−8
src/Yoga/Om/Layer.purs+12−8 test/Test/Main.purs+100
src/Yoga/Om/Layer.purs+12−8
@@ -298,20 +298,22 @@ withScoped layer callback = bracket acquire release use
298298-- =============================================================================
299299
300300combineRequirements
301− :: forall req1 req2 prov1 prov2 err1 err2 provMerged provMergedRaw reqMerged reqDeduped _req1 _req2 _err1 _err2
301+ :: forall req1 req2 prov1 prov2 err1 err2 provMerged provMergedRaw reqMerged reqDeduped errMerged errDeduped _req1 _req2 _err1 _err2
302302 . Union req1 req2 reqMerged
303303 => Nub reqMerged reqDeduped
304304 => Union req1 _req1 reqDeduped
305305 => Union req2 _req2 reqDeduped
306− => Union err1 _err1 ()
307− => Union err2 _err2 ()
306+ => Union err1 err2 errMerged
307+ => Nub errMerged errDeduped
308+ => Union err1 _err1 errDeduped
309+ => Union err2 _err2 errDeduped
308310 => Union prov1 prov2 provMergedRaw
309311 => Nub provMergedRaw provMerged
310312 => Keys req1
311313 => Keys req2
312314 => OmLayer req1 prov1 err1
313315 -> OmLayer req2 prov2 err2
314− -> OmLayer reqDeduped provMerged ()
316+ -> OmLayer reqDeduped provMerged errDeduped
315317combineRequirements layer1 layer2 = OmLayer (freshId unit) do
316318 rec1 <- Om.expand (buildLayer layer1)
317319 rec2 <- Om.expand (buildLayer layer2)
@@ -322,11 +324,13 @@ combineRequirements layer1 layer2 = OmLayer (freshId unit) do
322324-- =============================================================================
323325
324326provide
325− :: forall req prov1 prov2 err1 err2 req2 reqOut _req _prov1 _err1 _err2
327+ :: forall req prov1 prov2 err1 err2 req2 reqOut errMerged errDeduped _req _prov1 _err1 _err2
326328 . Union req _req req
327329 => Union prov1 _prov1 prov1
328− => Union err1 _err1 ()
329− => Union err2 _err2 ()
330+ => Union err1 err2 errMerged
331+ => Nub errMerged errDeduped
332+ => Union err1 _err1 errDeduped
333+ => Union err2 _err2 errDeduped
330334 => Union prov1 req reqOut
331335 => Nub reqOut reqOut
332336 => Union req2 _prov1 reqOut
@@ -335,7 +339,7 @@ provide
335339 => Keys req2
336340 => OmLayer req2 prov2 err2
337341 -> OmLayer req prov1 err1
338− -> OmLayer req prov2 ()
342+ -> OmLayer req prov2 errDeduped
339343provide l2 l1 = OmLayer (freshId unit) do
340344 prov1 <- Om.expand (buildLayer l1)
341345 Om.expand (buildLayer l2) # Om.widenCtx prov1
test/Test/Main.purs+100
@@ -13,6 +13,8 @@ import Test.Spec (describe, it)
1313import Test.Spec.Assertions (shouldEqual)
1414import Test.Spec.Reporter (consoleReporter)
1515import Test.Spec.Runner (runSpec)
16+import Data.Either (Either(..))
17+import Data.Variant (match)
1618import Yoga.Om as Om
1719import Yoga.Om.Layer (OmLayer, Scope, makeLayer, makeScopedLayer, bracketLayer, fresh, combineRequirements, runLayer, runScoped, withScoped, provide)
1820
@@ -305,3 +307,101 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
305307 result.y `shouldEqual` "43"
306308 finalLog <- liftEffect $ Ref.read log
307309 finalLog `shouldEqual` [ "acquire", "release" ]
310+
311+ describe "Error Channel" do
312+
313+ it "composes layers with different error types" do
314+ let
315+ dbLayer :: OmLayer () (db :: String) (dbError :: String)
316+ dbLayer = makeLayer do
317+ pure { db: "connected" }
318+
319+ cacheLayer :: OmLayer () (cache :: String) (cacheError :: String)
320+ cacheLayer = makeLayer do
321+ pure { cache: "ready" }
322+
323+ combined :: OmLayer () (db :: String, cache :: String) (dbError :: String, cacheError :: String)
324+ combined = combineRequirements dbLayer cacheLayer
325+
326+ result <- Om.runOm {}
327+ { exception: \_ -> pure { db: "", cache: "" }
328+ , dbError: \_ -> pure { db: "", cache: "" }
329+ , cacheError: \_ -> pure { db: "", cache: "" }
330+ }
331+ (runLayer {} combined)
332+ result.db `shouldEqual` "connected"
333+ result.cache `shouldEqual` "ready"
334+
335+ it "propagates errors from the first layer" do
336+ let
337+ failingLayer :: OmLayer () (db :: String) (dbError :: String)
338+ failingLayer = makeLayer do
339+ Om.throw { dbError: "connection refused" }
340+
341+ okLayer :: OmLayer () (cache :: String) ()
342+ okLayer = makeLayer do
343+ pure { cache: "ready" }
344+
345+ combined = combineRequirements failingLayer okLayer
346+
347+ result <- Om.runReader {} (runLayer {} combined)
348+ # liftAff
349+ case result of
350+ Left err -> do
351+ let
352+ msg = err # match
353+ { exception: \_ -> "exception"
354+ , dbError: \e -> e
355+ }
356+ msg `shouldEqual` "connection refused"
357+ Right _ -> "should have failed" `shouldEqual` "but succeeded"
358+
359+ it "propagates errors from the second layer" do
360+ let
361+ okLayer :: OmLayer () (db :: String) ()
362+ okLayer = makeLayer do
363+ pure { db: "connected" }
364+
365+ failingLayer :: OmLayer () (cache :: String) (cacheError :: String)
366+ failingLayer = makeLayer do
367+ Om.throw { cacheError: "cache unavailable" }
368+
369+ combined = combineRequirements okLayer failingLayer
370+
371+ result <- Om.runReader {} (runLayer {} combined)
372+ # liftAff
373+ case result of
374+ Left err -> do
375+ let
376+ msg = err # match
377+ { exception: \_ -> "exception"
378+ , cacheError: \e -> e
379+ }
380+ msg `shouldEqual` "cache unavailable"
381+ Right _ -> "should have failed" `shouldEqual` "but succeeded"
382+
383+ it "propagates errors through vertical composition" do
384+ let
385+ baseLayer :: OmLayer () (base :: String) (baseError :: String)
386+ baseLayer = makeLayer do
387+ Om.throw { baseError: "base failed" }
388+
389+ upperLayer :: OmLayer (base :: String) (upper :: String) (upperError :: String)
390+ upperLayer = makeLayer do
391+ { base } <- Om.ask
392+ pure { upper: base <> "-extended" }
393+
394+ composed = upperLayer `provide` baseLayer
395+
396+ result <- Om.runReader {} (runLayer {} composed)
397+ # liftAff
398+ case result of
399+ Left err -> do
400+ let
401+ msg = err # match
402+ { exception: \_ -> "exception"
403+ , baseError: \e -> e
404+ , upperError: \_ -> "upper"
405+ }
406+ msg `shouldEqual` "base failed"
407+ Right _ -> "should have failed" `shouldEqual` "but succeeded"