git.iko.soy/rowtype-yoga/purescript-yoga-om-layer
on main help sign in
Split runScoped/withScoped, clean up where clauses
commit56fdc71e4abb5e6789cea4e4f4719790267b0baa authorMark <mark.eibes@gmail.com> dateFri 13 Feb 2026, 20:34 (8 months ago) parent6d86a8f
4 files changed+39−51
spago.lock−6 spago.yaml−2 src/Yoga/Om/Layer.purs+30−33 test/Test/Main.purs+9−10
spago.lock−6
@@ -14,9 +14,6 @@
1414 {
1515 "foldable-traversable": ">=6.0.0 <7.0.0"
1616 },
17− {
18− "newtype": ">=5.0.0 <6.0.0"
19− },
2017 {
2118 "prelude": ">=6.0.2 <7.0.0"
2219 },
@@ -26,9 +23,6 @@
2623 {
2724 "record-studio": ">=1.0.4 <2.0.0"
2825 },
29− {
30− "tuples": ">=7.0.0 <8.0.0"
31− },
3226 {
3327 "type-equality": ">=4.0.1 <5.0.0"
3428 },
spago.yaml−2
@@ -12,11 +12,9 @@ package:
1212 - aff: ">=7.1.0 <8.0.0"
1313 - arrays: ">=7.3.0 <8.0.0"
1414 - foldable-traversable: ">=6.0.0 <7.0.0"
15− - newtype: ">=5.0.0 <6.0.0"
1615 - prelude: ">=6.0.2 <7.0.0"
1716 - record: ">=4.0.0 <5.0.0"
1817 - record-studio: ">=1.0.4 <2.0.0"
19− - tuples: ">=7.0.0 <8.0.0"
2018 - type-equality: ">=4.0.1 <5.0.0"
2119 - unsafe-coerce: ">=6.0.0 <7.0.0"
2220 - yoga-om: ">=1.2.2 <2.0.0"
src/Yoga/Om/Layer.purs+30−33
@@ -1,8 +1,8 @@
11-- @inline export makeLayer arity=1
22-- @inline export runLayer arity=2
33module Yoga.Om.Layer
4− ( OmLayer(..)
5− , Finalizers(..)
4+ ( OmLayer
5+ , Finalizers
66 , makeLayer
77 , makeScopedLayer
88 , bracketLayer
@@ -20,8 +20,6 @@ module Yoga.Om.Layer
2020import Prelude
2121
2222import Data.Array as Array
23−import Data.Newtype (class Newtype)
24−import Data.Tuple.Nested ((/\), type (/\))
2523import Data.Foldable (for_)
2624import Effect.Aff (Aff, generalBracket)
2725import Effect.Aff.Class (liftAff)
@@ -43,8 +41,6 @@ import Yoga.Om as Om
4341-- | executed in reverse (LIFO) by `runScoped`.
4442newtype Finalizers = Finalizers (Array (Aff Unit))
4543
46−derive instance Newtype Finalizers _
47−
4844instance Semigroup Finalizers where
4945 append (Finalizers a) (Finalizers b) = Finalizers (a <> b)
5046
@@ -56,11 +52,11 @@ instance Monoid Finalizers where
5652-- carries finalizers for resource cleanup
5753-- =============================================================================
5854
55+type Built prov = { provisions :: Record prov, finalizers :: Finalizers }
56+
5957-- | A layer that requires dependencies (req), provides services (prov),
6058-- | and may carry finalizers for scoped resource management.
61−newtype OmLayer req prov err = OmLayer (Om (Record req) err (Record prov /\ Finalizers))
62−
63−derive instance Newtype (OmLayer req prov err) _
59+data OmLayer req prov err = OmLayer (Om (Record req) err (Built prov))
6460
6561-- =============================================================================
6662-- Layer constructors
@@ -68,7 +64,7 @@ derive instance Newtype (OmLayer req prov err) _
6864
6965-- | Create a layer from an Om computation (no finalizers).
7066makeLayer :: forall req prov err. Om (Record req) err (Record prov) -> OmLayer req prov err
71−makeLayer om = OmLayer (om <#> \r -> r /\ mempty)
67+makeLayer om = OmLayer (om <#> \provisions -> { provisions, finalizers: mempty })
7268
7369-- | Create a scoped layer with acquire/release semantics.
7470-- | The release function is guaranteed to run when `runScoped` closes the scope.
@@ -78,10 +74,9 @@ makeScopedLayer
7874 -> (Record prov -> Aff Unit)
7975 -> OmLayer req prov err
8076makeScopedLayer acquire release = OmLayer do
81− prov <- acquire
82− pure (prov /\ finalizer prov)
83− where
84− finalizer prov = Finalizers [ release prov ]
77+ provisions <- acquire
78+ let finalizers = Finalizers [ release provisions ]
79+ pure { provisions, finalizers }
8580
8681-- | Bracket-style scoped layer: acquire a resource, build provisions from it,
8782-- | and register a finalizer for the resource.
@@ -93,10 +88,9 @@ bracketLayer
9388 -> OmLayer req prov err
9489bracketLayer acquire release use = OmLayer do
9590 resource <- acquire
96− prov <- use resource
97− pure (prov /\ finalizer resource)
98− where
99− finalizer resource = Finalizers [ release resource ]
91+ provisions <- use resource
92+ let finalizers = Finalizers [ release resource ]
93+ pure { provisions, finalizers }
10094
10195-- =============================================================================
10296-- Custom Type Errors for Missing Dependencies
@@ -180,10 +174,10 @@ runLayer
180174 => Record available
181175 -> OmLayer req prov err
182176 -> Om (Record available) err (Record prov)
183−runLayer _ctx layer = unsafeCastLayer layer <#> \(prov /\ _) -> prov
177+runLayer _ctx (OmLayer om) = widenCtx om <#> _.provisions
184178 where
185− unsafeCastLayer :: OmLayer req prov err -> Om (Record available) err (Record prov /\ Finalizers)
186− unsafeCastLayer = unsafeCoerce
179+ widenCtx :: Om (Record req) err (Built prov) -> Om (Record available) err (Built prov)
180+ widenCtx = unsafeCoerce
187181
188182-- | Build a fully-provided layer, run finalizers, return the provisions.
189183-- | Useful when you just need the built services.
@@ -204,13 +198,13 @@ withScoped
204198withScoped (OmLayer om) callback = generalBracket acquire conditions use
205199 where
206200 acquire = Om.runOm {} errorHandlers om
207− errorHandlers = { exception: \_ -> pure (unsafeCoerce {} /\ mempty) }
201+ errorHandlers = { exception: \_ -> pure { provisions: unsafeCoerce {}, finalizers: mempty } }
208202 conditions =
209− { completed: \_ (_ /\ fins) -> runFinalizers fins
210− , failed: \_ (_ /\ fins) -> runFinalizers fins
211− , killed: \_ (_ /\ fins) -> runFinalizers fins
203+ { completed: \_ built -> runFinalizers built.finalizers
204+ , failed: \_ built -> runFinalizers built.finalizers
205+ , killed: \_ built -> runFinalizers built.finalizers
212206 }
213− use (prov /\ _) = callback prov
207+ use built = callback built.provisions
214208
215209-- | Run finalizers in reverse order (LIFO).
216210runFinalizers :: Finalizers -> Aff Unit
@@ -236,9 +230,11 @@ combineRequirements
236230 -> OmLayer req2 prov2 err2
237231 -> OmLayer reqDeduped provMerged ()
238232combineRequirements (OmLayer build1) (OmLayer build2) = OmLayer do
239− prov1 /\ fins1 <- Om.expand build1
240− prov2 /\ fins2 <- Om.expand build2
241− pure (Record.merge prov1 prov2 /\ (fins1 <> fins2))
233+ built1 <- Om.expand build1
234+ built2 <- Om.expand build2
235+ let provisions = Record.merge built1.provisions built2.provisions
236+ let finalizers = built1.finalizers <> built2.finalizers
237+ pure { provisions, finalizers }
242238
243239-- =============================================================================
244240-- Vertical composition — feed output of one layer into input of another
@@ -256,11 +252,12 @@ provide
256252 -> OmLayer req prov1 err1
257253 -> OmLayer req prov2 ()
258254provide (OmLayer layer2) (OmLayer layer1) = OmLayer do
259− prov1 /\ fins1 <- Om.expand layer1
260− prov2 /\ fins2 <- runLayer2 prov1
261− pure (prov2 /\ (fins1 <> fins2))
255+ built1 <- Om.expand layer1
256+ built2 <- runLayer2 built1.provisions
257+ let finalizers = built1.finalizers <> built2.finalizers
258+ pure { provisions: built2.provisions, finalizers }
262259 where
263260 runLayer2 ctx = liftAff $ Om.runOm ctx errorHandlers (Om.expand layer2)
264− errorHandlers = { exception: \_ -> pure (unsafeCoerce {} /\ mempty) }
261+ errorHandlers = { exception: \_ -> pure { provisions: unsafeCoerce {}, finalizers: mempty } }
265262
266263infixl 9 provide as >->
test/Test/Main.purs+9−10
@@ -2,8 +2,6 @@ module Test.Main where
22
33import Prelude
44
5−import Data.Newtype (un)
6−import Data.Tuple.Nested ((/\))
75import Effect (Effect)
86import Effect.Aff (launchAff_, throwError, try)
97import Effect.Aff.Class (liftAff)
@@ -16,7 +14,7 @@ import Test.Spec.Assertions (shouldEqual)
1614import Test.Spec.Reporter (consoleReporter)
1715import Test.Spec.Runner (runSpec)
1816import Yoga.Om as Om
19−import Yoga.Om.Layer (OmLayer(..), Finalizers, makeLayer, makeScopedLayer, bracketLayer, combineRequirements, runScoped, withScoped, provide)
17+import Yoga.Om.Layer (OmLayer, makeLayer, makeScopedLayer, bracketLayer, combineRequirements, runLayer, runScoped, withScoped, provide)
2018
2119-- Example types
2220type Config = { port :: Int, host :: String }
@@ -95,13 +93,14 @@ main = launchAff_ $ runSpec [ consoleReporter ] do
9593
9694 pure { logger, database }
9795
98− -- Run with the context
99− result /\ _ <- un OmLayer combinedLayer
100− # Om.runOm
101− { config: { port: 5432, host: "localhost" }
102− , accessLog: accessLog
103− }
104− { exception: \_ -> pure ({ logger: { log: \_ -> pure unit }, database: { query: \_ -> pure [] } } /\ (mempty :: Finalizers)) }
96+ let
97+ ctx =
98+ { config: { port: 5432, host: "localhost" }
99+ , accessLog: accessLog
100+ }
101+ result <- runLayer ctx combinedLayer
102+ # Om.runOm ctx
103+ { exception: \_ -> pure { logger: { log: \_ -> pure unit }, database: { query: \_ -> pure [] } } }
105104
106105 -- Check the access log
107106 finalLog <- liftEffect $ Ref.read accessLog