@@ -14,6 +14,9 @@ module Yoga.Om.Layer
14 , combineRequirements
14 , combineRequirements
17+ , class CheckMemoKeys
18+ , class CheckMemoKeysList
19+ , class CheckKeyNotDuplicated
17 , class CheckAllProvided
20 , class CheckAllProvided
18 , class CheckAllLabelsExist
21 , class CheckAllLabelsExist
19 , class CheckLabelExists
22 , class CheckLabelExists
@@ -85,15 +88,16 @@ closeScope (Scope s) = do
86-- =============================================================================
89-- =============================================================================
88−-- | A layer that requires dependencies (req) and provides services (prov).
91+-- | A layer that requires dependencies (req), provides services (prov),
92+-- | and tracks memoization keys (memo) to prevent key collisions at compile time.
89−data OmLayer req prov err = OmLayer (Om (Record req) err (Record prov))
93+data OmLayer req prov err (memo :: Row Type) = OmLayer (Om (Record req) err (Record prov))
91-- =============================================================================
95-- =============================================================================
92-- Layer constructors
96-- Layer constructors
93-- =============================================================================
97-- =============================================================================
95-- | Create a layer from an Om computation.
99-- | Create a layer from an Om computation.
96−makeLayer :: forall req prov err. Om (Record req) err (Record prov) -> OmLayer req prov err
100+makeLayer :: forall req prov err. Om (Record req) err (Record prov) -> OmLayer req prov err ()
97makeLayer = OmLayer
101makeLayer = OmLayer
99-- | Create a scoped layer with acquire/release semantics.
103-- | Create a scoped layer with acquire/release semantics.
@@ -103,7 +107,7 @@ makeScopedLayer
103 :: forall req prov err
107 :: forall req prov err
104 . Om { scope :: Scope | req } err (Record prov)
108 . Om { scope :: Scope | req } err (Record prov)
105 -> (Record prov -> Aff Unit)
109 -> (Record prov -> Aff Unit)
106− -> OmLayer (scope :: Scope | req) prov err
110+ -> OmLayer (scope :: Scope | req) prov err ()
107makeScopedLayer acquire release = makeLayer do
111makeScopedLayer acquire release = makeLayer do
108 acquireRelease acquire release
112 acquireRelease acquire release
@@ -114,7 +118,7 @@ bracketLayer
114 . Om { scope :: Scope | req } err resource
118 . Om { scope :: Scope | req } err resource
115 -> (resource -> Aff Unit)
119 -> (resource -> Aff Unit)
116 -> (resource -> Om { scope :: Scope | req } err (Record prov))
120 -> (resource -> Om { scope :: Scope | req } err (Record prov))
117− -> OmLayer (scope :: Scope | req) prov err
121+ -> OmLayer (scope :: Scope | req) prov err ()
118bracketLayer acquire release use = makeLayer do
122bracketLayer acquire release use = makeLayer do
119 resource <- acquireRelease acquire (\r -> release r)
123 resource <- acquireRelease acquire (\r -> release r)
120 use resource
124 use resource
@@ -137,11 +141,11 @@ acquireRelease acquire release = do
137-- | If the same key has already been built in this scope, the cached result
141-- | If the same key has already been built in this scope, the cached result
138-- | is returned without re-running the layer.
142-- | is returned without re-running the layer.
140− :: forall sym req prov err
144+ :: forall sym req prov err memo
141 . IsSymbol sym
145 . IsSymbol sym
142 => Proxy sym
146 => Proxy sym
143− -> OmLayer (scope :: Scope | req) prov err
147+ -> OmLayer (scope :: Scope | req) prov err memo
144− -> OmLayer (scope :: Scope | req) prov err
148+ -> OmLayer (scope :: Scope | req) prov err (sym :: Record prov | memo)
145memoized _ (OmLayer om) = OmLayer do
149memoized _ (OmLayer om) = OmLayer do
146 { scope: Scope s } <- Om.ask
150 { scope: Scope s } <- Om.ask
147 let key = reflectSymbol (Proxy :: Proxy sym)
151 let key = reflectSymbol (Proxy :: Proxy sym)
@@ -153,6 +157,38 @@ memoized _ (OmLayer om) = OmLayer do
153 liftEffect $ Ref.modify_ (Map.insert key (toDynamic prov)) s.cache
157 liftEffect $ Ref.modify_ (Map.insert key (toDynamic prov)) s.cache
154 pure prov
158 pure prov
160+-- =============================================================================
161+-- Memo key safety — reject duplicate keys with different types
162+-- =============================================================================
164+class CheckMemoKeys (row :: Row Type)
166+instance (RowToList row rl, CheckMemoKeysList rl) => CheckMemoKeys row
168+class CheckMemoKeysList (rl :: RowList Type)
170+instance CheckMemoKeysList Nil
173+ ( CheckMemoKeysList tail
174+ , CheckKeyNotDuplicated label ty tail
176+ CheckMemoKeysList (Cons label ty tail)
178+class CheckKeyNotDuplicated (label :: Symbol) (ty :: Type) (rest :: RowList Type)
180+instance CheckKeyNotDuplicated label ty Nil
184+ , CheckKeyNotDuplicated label ty tail
186+ CheckKeyNotDuplicated label ty (Cons label ty' tail)
189+ CheckKeyNotDuplicated label ty tail =>
190+ CheckKeyNotDuplicated label ty (Cons otherLabel otherTy tail)
156-- =============================================================================
192-- =============================================================================
157-- Custom Type Errors for Missing Dependencies
193-- Custom Type Errors for Missing Dependencies
158-- =============================================================================
194-- =============================================================================
@@ -229,10 +265,10 @@ else instance
230-- | Run a layer with a given context, with custom error if requirements aren't met.
266-- | Run a layer with a given context, with custom error if requirements aren't met.
232− :: forall req prov err available
268+ :: forall req prov err available memo
233 . CheckAllProvided req available
269 . CheckAllProvided req available
234 => Record available
270 => Record available
235− -> OmLayer req prov err
271+ -> OmLayer req prov err memo
236 -> Om (Record available) err (Record prov)
272 -> Om (Record available) err (Record prov)
237runLayer _ctx (OmLayer om) = widenCtx om
273runLayer _ctx (OmLayer om) = widenCtx om
@@ -242,8 +278,8 @@ runLayer _ctx (OmLayer om) = widenCtx om
242-- | Build a fully-provided scoped layer, return the provisions.
278-- | Build a fully-provided scoped layer, return the provisions.
243-- | All finalizers run after the provisions are returned.
279-- | All finalizers run after the provisions are returned.
245− :: forall prov
281+ :: forall prov memo
246− . OmLayer (scope :: Scope) prov ()
282+ . OmLayer (scope :: Scope) prov () memo
247 -> Aff (Record prov)
283 -> Aff (Record prov)
248runScoped layer = withScoped layer pure
284runScoped layer = withScoped layer pure
@@ -252,8 +288,8 @@ runScoped layer = withScoped layer pure
252-- | running all finalizers in reverse order — whether by success, failure,
288-- | running all finalizers in reverse order — whether by success, failure,
253-- | or interruption.
289-- | or interruption.
254withScoped
290withScoped
255− :: forall prov a
291+ :: forall prov a memo
256− . OmLayer (scope :: Scope) prov ()
292+ . OmLayer (scope :: Scope) prov () memo
257 -> (Record prov -> Aff a)
293 -> (Record prov -> Aff a)
259withScoped (OmLayer om) callback = bracket acquire release use
295withScoped (OmLayer om) callback = bracket acquire release use
@@ -270,7 +306,7 @@ withScoped (OmLayer om) callback = bracket acquire release use
270-- =============================================================================
306-- =============================================================================
272combineRequirements
308combineRequirements
273− :: forall req1 req2 prov1 prov2 err1 err2 provMerged provMergedRaw reqMerged reqDeduped _req1 _req2 _err1 _err2
309+ :: forall req1 req2 prov1 prov2 err1 err2 memo1 memo2 provMerged provMergedRaw reqMerged reqDeduped memoMerged memoDeduped _req1 _req2 _err1 _err2
274 . Union req1 req2 reqMerged
310 . Union req1 req2 reqMerged
275 => Nub reqMerged reqDeduped
311 => Nub reqMerged reqDeduped
276 => Union req1 _req1 reqDeduped
312 => Union req1 _req1 reqDeduped
@@ -279,11 +315,14 @@ combineRequirements
279 => Union err2 _err2 ()
315 => Union err2 _err2 ()
280 => Union prov1 prov2 provMergedRaw
316 => Union prov1 prov2 provMergedRaw
281 => Nub provMergedRaw provMerged
317 => Nub provMergedRaw provMerged
318+ => Union memo1 memo2 memoMerged
319+ => CheckMemoKeys memoMerged
320+ => Nub memoMerged memoDeduped
282 => Keys req1
321 => Keys req1
283 => Keys req2
322 => Keys req2
284− => OmLayer req1 prov1 err1
323+ => OmLayer req1 prov1 err1 memo1
285− -> OmLayer req2 prov2 err2
324+ -> OmLayer req2 prov2 err2 memo2
286− -> OmLayer reqDeduped provMerged ()
325+ -> OmLayer reqDeduped provMerged () memoDeduped
287combineRequirements (OmLayer build1) (OmLayer build2) = OmLayer do
326combineRequirements (OmLayer build1) (OmLayer build2) = OmLayer do
288 rec1 <- Om.expand build1
327 rec1 <- Om.expand build1
289 rec2 <- Om.expand build2
328 rec2 <- Om.expand build2
@@ -294,7 +333,7 @@ combineRequirements (OmLayer build1) (OmLayer build2) = OmLayer do
294-- =============================================================================
333-- =============================================================================
297− :: forall req prov1 prov2 err1 err2 req2 reqOut _req _prov1 _err1 _err2
336+ :: forall req prov1 prov2 err1 err2 req2 reqOut memo1 memo2 memoMerged memoDeduped _req _prov1 _err1 _err2
298 . Union req _req req
337 . Union req _req req
299 => Union prov1 _prov1 prov1
338 => Union prov1 _prov1 prov1
300 => Union err1 _err1 ()
339 => Union err1 _err1 ()
@@ -302,12 +341,15 @@ provide
302 => Union prov1 req reqOut
341 => Union prov1 req reqOut
303 => Nub reqOut reqOut
342 => Nub reqOut reqOut
304 => Union req2 _prov1 reqOut
343 => Union req2 _prov1 reqOut
344+ => Union memo1 memo2 memoMerged
345+ => CheckMemoKeys memoMerged
346+ => Nub memoMerged memoDeduped
305 => Keys req
347 => Keys req
306 => Keys prov1
348 => Keys prov1
307 => Keys req2
349 => Keys req2
308− => OmLayer req2 prov2 err2
350+ => OmLayer req2 prov2 err2 memo2
309− -> OmLayer req prov1 err1
351+ -> OmLayer req prov1 err1 memo1
310− -> OmLayer req prov2 ()
352+ -> OmLayer req prov2 () memoDeduped
311provide (OmLayer layer2) (OmLayer layer1) = OmLayer do
353provide (OmLayer layer2) (OmLayer layer1) = OmLayer do
312 prov1 <- Om.expand layer1
354 prov1 <- Om.expand layer1
313 Om.expand layer2 # Om.widenCtx prov1
355 Om.expand layer2 # Om.widenCtx prov1