effectful 2.0.0.0 → 2.1.0.0
raw patch · 5 files changed
+49/−18 lines, 5 filesdep ~effectful-corePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: effectful-core
API changes (from Hackage documentation)
Files
- CHANGELOG.md +6/−0
- effectful.cabal +2/−2
- src/Effectful/Concurrent/Effect.hs +2/−2
- tests/EnvTests.hs +22/−10
- tests/ReaderTests.hs +17/−4
CHANGELOG.md view
@@ -1,3 +1,9 @@+# effectful-2.1.0.0 (2022-08-22)+* Include the `e :> localEs` constraint in the `EffectHandler` to allow more+ flexibility in handling higher order effects.+* Do not include internal stack frames in `throwError` from+ `Effectful.Error.Dynamic`.+ # effectful-2.0.0.0 (2022-08-12) * Make storage references in the environment immutable. * Remove `checkSizeEnv` and `forkEnv` from
effectful.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 build-type: Simple name: effectful-version: 2.0.0.0+version: 2.1.0.0 license: BSD-3-Clause license-file: LICENSE category: Control@@ -67,7 +67,7 @@ , async >= 2.2.2 , bytestring >= 0.10 , directory >= 1.3.2- , effectful-core >= 2.0.0.0 && < 2.0.1.0+ , effectful-core >= 2.1.0.0 && < 2.1.1.0 , process >= 1.6.9 , time >= 1.9.2
src/Effectful/Concurrent/Effect.hs view
@@ -44,9 +44,9 @@ -- it's inside the 'Effectful.Reader.Static.local' block, even though the parent -- thread already got out of it. ----- This is because the value provided by the 'Effectful.Reader.Static.Reader'+-- This is because the value provided by the t'Effectful.Reader.Static.Reader' -- effect is thread local, i.e. each thread manages its own version of it. For--- the 'Effectful.Reader.Static.Reader' it is the only reasonable behavior, it+-- the t'Effectful.Reader.Static.Reader' it is the only reasonable behavior, it -- wouldn't be very useful if its "read only" value was affected by calls to -- 'Effectful.Reader.Static.local' from its parent or child threads. --
tests/EnvTests.hs view
@@ -16,7 +16,7 @@ [ testCase "tailEnv works" test_tailEnv , testCase "subsume works" test_subsumeEnv , testCase "inject works" test_injectEnv- , testCase "unsafeCoerce doesn't leak" test_noUnsafeCoerce+ , testCase "unsafeCoerce doesn't work" test_noUnsafeCoerce , testCase "interpose works" test_interpose ] @@ -56,17 +56,29 @@ modify @Int (+4) raise $ modify @Int (+8) +----------------------------------------+ test_noUnsafeCoerce :: Assertion test_noUnsafeCoerce = do- r <- try @ErrorCall $ funToInt id- assertBool "unsafeCoerce leaks" (isLeft r)- where- funToInt :: (a -> b) -> IO Int- funToInt f = runEff $ do- oops <- runReader @Int 0 $ do- withEffToIO $ \unlift -> do- pure . unlift $ ask @Int- runReader f $ liftIO oops+ r <- try @ErrorCall . evaluate $ unsafeCoerce @Int 'a'+ assertBool "unsafeCoerce" (isLeft r)++unsafeCoerce :: forall b a. a -> b+unsafeCoerce a = runPureEff $ do+ -- 'oops' gains access to the effect stack with Reader (Box b) via the+ -- unlifting function that escaped its scope. The problem here is that this+ -- effect is no longer in scope.+ oops <- runReader @(Box b) (Box undefined) $ do+ raiseWith SeqUnlift $ \unlift -> do+ pure . unlift $ ask @(Box b)+ -- Put Reader (Box a) where the Reader (Box b) was before and attempt to+ -- retrieve 'a' coerced to 'b'. It fails because 'getLocation' in+ -- 'Effectful.Internal.Env' checks that version of the reference is the same+ -- as version of the effect.+ Box b <- runReader (Box a) $ raise oops+ pure b++data Box a = Box a ----------------------------------------
tests/ReaderTests.hs view
@@ -5,12 +5,13 @@ import Effectful import Effectful.Dispatch.Dynamic-import Effectful.Reader.Static+import Effectful.Reader.Dynamic import qualified Utils as U readerTests :: TestTree readerTests = testGroup "Reader"- [ testCase "local works in handlers" test_localInHandler+ [ testCase "local works in handlers (dynamic/static)" $ test_localInHandler runReader+ , testCase "local works in handlers (dynamic/pure)" $ test_localInHandler runPureReader ] data SomeEff :: Effect where@@ -18,9 +19,21 @@ type instance DispatchOf SomeEff = Dynamic -test_localInHandler :: Assertion-test_localInHandler = runEff . runReader "global" . interpret f $ do+test_localInHandler+ :: (forall r es a. r -> Eff (Reader r : es) a -> Eff es a)+ -> Assertion+test_localInHandler runR = runEff . runR "global" . interpret f $ do local (const "local") $ send SomeAction where f :: [IOE, Reader String] :>> es => EffectHandler SomeEff es f _ SomeAction = U.assertEqual "expected result" "local" =<< ask++-- | Purely dynamic Reader for testing purposes.+runPureReader :: r -> Eff (Reader r : es) a -> Eff es a+runPureReader r0 = interpret (handler r0)+ where+ handler :: r -> EffectHandler (Reader r) handlerEs+ handler r env = \case+ Ask -> pure r+ Local f m -> localSeqUnlift env $ \unlift -> do+ unlift $ interpose (handler $ f r) m