packages feed

polysemy 1.2.1.0 → 1.2.2.0

raw patch · 6 files changed

+243/−115 lines, 6 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Polysemy.Async: sequenceConcurrently :: forall t r a. (Traversable t, Member Async r) => t (Sem r a) -> Sem r (t (Maybe a))
+ Polysemy.AtomicState: atomicGets :: forall s s' r. Member (AtomicState s) r => (s -> s') -> Sem r s'
- Polysemy.Error: catch :: forall e_aulO r_aunJ a_aulQ. MemberWithError (Error e_aulO) r_aunJ => Sem r_aunJ a_aulQ -> (e_aulO -> Sem r_aunJ a_aulQ) -> Sem r_aunJ a_aulQ
+ Polysemy.Error: catch :: forall e_autz r_auvu a_autB. MemberWithError (Error e_autz) r_auvu => Sem r_auvu a_autB -> (e_autz -> Sem r_auvu a_autB) -> Sem r_auvu a_autB
- Polysemy.Error: throw :: forall e_aulL r_aunH a_aulN. MemberWithError (Error e_aulL) r_aunH => e_aulL -> Sem r_aunH a_aulN
+ Polysemy.Error: throw :: forall e_autw r_auvs a_auty. MemberWithError (Error e_autw) r_auvs => e_autw -> Sem r_auvs a_auty
- Polysemy.Input: input :: forall i_aEuh r_aEva. MemberWithError (Input i_aEuh) r_aEva => Sem r_aEva i_aEuh
+ Polysemy.Input: input :: forall i_aEKY r_aELR. MemberWithError (Input i_aEKY) r_aELR => Sem r_aELR i_aEKY
- Polysemy.Internal.Writer: listen :: forall o_awn1 r_awpp a_Xwn4. MemberWithError (Writer o_awn1) r_awpp => Sem r_awpp a_Xwn4 -> Sem r_awpp (o_awn1, a_Xwn4)
+ Polysemy.Internal.Writer: listen :: forall o_awuM r_awxa a_XwuP. MemberWithError (Writer o_awuM) r_awxa => Sem r_awxa a_XwuP -> Sem r_awxa (o_awuM, a_XwuP)
- Polysemy.Internal.Writer: pass :: forall o_awn5 r_awpr a_awn6. MemberWithError (Writer o_awn5) r_awpr => Sem r_awpr (o_awn5 -> o_awn5, a_awn6) -> Sem r_awpr a_awn6
+ Polysemy.Internal.Writer: pass :: forall o_awuQ r_awxc a_awuR. MemberWithError (Writer o_awuQ) r_awxc => Sem r_awxc (o_awuQ -> o_awuQ, a_awuR) -> Sem r_awxc a_awuR
- Polysemy.Internal.Writer: tell :: forall o_awmZ r_awpn. MemberWithError (Writer o_awmZ) r_awpn => o_awmZ -> Sem r_awpn ()
+ Polysemy.Internal.Writer: tell :: forall o_awuK r_awx8. MemberWithError (Writer o_awuK) r_awx8 => o_awuK -> Sem r_awx8 ()
- Polysemy.Output: output :: forall o_aCYC r_aCZs. MemberWithError (Output o_aCYC) r_aCZs => o_aCYC -> Sem r_aCZs ()
+ Polysemy.Output: output :: forall o_aDfj r_aDg9. MemberWithError (Output o_aDfj) r_aDg9 => o_aDfj -> Sem r_aDg9 ()
- Polysemy.Reader: ask :: forall i_aER1 r_aESy. MemberWithError (Reader i_aER1) r_aESy => Sem r_aESy i_aER1
+ Polysemy.Reader: ask :: forall i_aF7I r_aF9f. MemberWithError (Reader i_aF7I) r_aF9f => Sem r_aF9f i_aF7I
- Polysemy.Reader: local :: forall i_aER3 r_aESz a_aER5. MemberWithError (Reader i_aER3) r_aESz => (i_aER3 -> i_aER3) -> Sem r_aESz a_aER5 -> Sem r_aESz a_aER5
+ Polysemy.Reader: local :: forall i_aF7K r_aF9g a_aF7M. MemberWithError (Reader i_aF7K) r_aF9g => (i_aF7K -> i_aF7K) -> Sem r_aF9g a_aF7M -> Sem r_aF9g a_aF7M
- Polysemy.Resource: bracket :: forall r_azRb a_XzOY c_XzP0 b_azOZ. MemberWithError Resource r_azRb => Sem r_azRb a_XzOY -> (a_XzOY -> Sem r_azRb c_XzP0) -> (a_XzOY -> Sem r_azRb b_azOZ) -> Sem r_azRb b_azOZ
+ Polysemy.Resource: bracket :: forall r_azYW a_XzWJ c_XzWL b_azWK. MemberWithError Resource r_azYW => Sem r_azYW a_XzWJ -> (a_XzWJ -> Sem r_azYW c_XzWL) -> (a_XzWJ -> Sem r_azYW b_azWK) -> Sem r_azYW b_azWK
- Polysemy.Resource: bracketOnError :: forall r_azRf a_XzP2 c_XzP4 b_azP3. MemberWithError Resource r_azRf => Sem r_azRf a_XzP2 -> (a_XzP2 -> Sem r_azRf c_XzP4) -> (a_XzP2 -> Sem r_azRf b_azP3) -> Sem r_azRf b_azP3
+ Polysemy.Resource: bracketOnError :: forall r_azZ0 a_XzWN c_XzWP b_azWO. MemberWithError Resource r_azZ0 => Sem r_azZ0 a_XzWN -> (a_XzWN -> Sem r_azZ0 c_XzWP) -> (a_XzWN -> Sem r_azZ0 b_azWO) -> Sem r_azZ0 b_azWO
- Polysemy.State: get :: forall s_aBvd r_aBwG. MemberWithError (State s_aBvd) r_aBwG => Sem r_aBwG s_aBvd
+ Polysemy.State: get :: forall s_aBLU r_aBNn. MemberWithError (State s_aBLU) r_aBNn => Sem r_aBNn s_aBLU
- Polysemy.State: put :: forall s_aBvf r_aBwH. MemberWithError (State s_aBvf) r_aBwH => s_aBvf -> Sem r_aBwH ()
+ Polysemy.State: put :: forall s_aBLW r_aBNo. MemberWithError (State s_aBLW) r_aBNo => s_aBLW -> Sem r_aBNo ()
- Polysemy.Trace: trace :: forall r_aGlI. MemberWithError Trace r_aGlI => String -> Sem r_aGlI ()
+ Polysemy.Trace: trace :: forall r_aGFM. MemberWithError Trace r_aGFM => String -> Sem r_aGFM ()
- Polysemy.Writer: listen :: forall o_awn1 r_awpp a_Xwn4. MemberWithError (Writer o_awn1) r_awpp => Sem r_awpp a_Xwn4 -> Sem r_awpp (o_awn1, a_Xwn4)
+ Polysemy.Writer: listen :: forall o_awuM r_awxa a_XwuP. MemberWithError (Writer o_awuM) r_awxa => Sem r_awxa a_XwuP -> Sem r_awxa (o_awuM, a_XwuP)
- Polysemy.Writer: pass :: forall o_awn5 r_awpr a_awn6. MemberWithError (Writer o_awn5) r_awpr => Sem r_awpr (o_awn5 -> o_awn5, a_awn6) -> Sem r_awpr a_awn6
+ Polysemy.Writer: pass :: forall o_awuQ r_awxc a_awuR. MemberWithError (Writer o_awuQ) r_awxc => Sem r_awxc (o_awuQ -> o_awuQ, a_awuR) -> Sem r_awxc a_awuR
- Polysemy.Writer: tell :: forall o_awmZ r_awpn. MemberWithError (Writer o_awmZ) r_awpn => o_awmZ -> Sem r_awpn ()
+ Polysemy.Writer: tell :: forall o_awuK r_awx8. MemberWithError (Writer o_awuK) r_awx8 => o_awuK -> Sem r_awx8 ()

Files

ChangeLog.md view
@@ -1,5 +1,13 @@ # Changelog for polysemy +## 1.2.2.0 (2019-10-22)++- Fixed a bug in `resourceToIO` and `resourceToIOFinal` that prevented the+    finalizers from being called in `BracketOnError` when the computation failed+    due to a `Sem` failure+- Added `atomicGets` (thanks to @googleson78)+- Added `sequenceConcurrently` to `Polysemy.Async` (thanks to @spacekitteh)+ ## 1.2.1.0 (2019-09-15)  - Added `InterpreterFor` (thanks to @bolt12)
polysemy.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 7ce7e4fdc1a3ebff15b38a550ed247a4ad04bb4c383ad4440212926b3eeea93c+-- hash: 6bdca16663a4fb1932ed4f9ec8736ba29a0355783b546db2811df338a9d21ae8  name:           polysemy-version:        1.2.1.0+version:        1.2.2.0 synopsis:       Higher-order, low-boilerplate, zero-cost free monads. description:    Please see the README on GitHub at <https://github.com/isovector/polysemy#readme> category:       Language
src/Polysemy/Async.hs view
@@ -8,6 +8,9 @@   , async   , await +    -- * Helpers+  , sequenceConcurrently+     -- * Interpretations   , asyncToIO   , asyncToIOFinal@@ -32,6 +35,17 @@   Await :: A.Async a -> Async m a  makeSem ''Async+++------------------------------------------------------------------------------+-- | Perform a sequence of effectful actions concurrently.+--+-- @since 1.2.2.0+sequenceConcurrently :: forall t r a. (Traversable t, Member Async r) =>+    t (Sem r a) -> Sem r (t (Maybe a))+sequenceConcurrently t = traverse async t >>= traverse await+{-# INLINABLE sequenceConcurrently #-}+  ------------------------------------------------------------------------------ -- | A more flexible --- though less performant ---
src/Polysemy/AtomicState.hs view
@@ -8,6 +8,7 @@   , atomicState   , atomicState'   , atomicGet+  , atomicGets   , atomicPut   , atomicModify   , atomicModify'@@ -47,6 +48,15 @@ atomicGet :: forall s r            . Member (AtomicState s) r           => Sem r s++------------------------------------------------------------------------------+-- | @since 1.2.2.0+atomicGets :: forall s s' r+            . Member (AtomicState s) r+           => (s -> s')+           -> Sem r s'+atomicGets = (<$> atomicGet)+{-# INLINE atomicGets #-}  ----------------------------------------------------------------------------- -- | A variant of 'atomicState' in which the computation is strict in the new
src/Polysemy/Resource.hs view
@@ -103,10 +103,23 @@     pure $ X.bracket a d u    BracketOnError alloc dealloc use -> do+    ins <- getInspectorS     a <- runS  alloc     d <- bindS dealloc     u <- bindS use-    pure $ X.bracketOnError a d u+    pure $+      X.bracketOnError+        a+        d+        (\x -> do+          result <- u x+          case inspect ins result of+            Just _ -> pure result+            Nothing -> do+              _ <- d x+              pure result+        )+ {-# INLINE resourceToIOFinal #-}  @@ -222,6 +235,7 @@           (done . mc)    BracketOnError a b c -> do+    ins <- getInspectorT     ma <- runT a     mb <- bindT b     mc <- bindT c@@ -232,6 +246,13 @@       X.bracketOnError           (done ma)           (\x -> done (mb x) >> finish)-          (done . mc)+          (\x -> do+            result <- done $ mc x+            case inspect ins result of+              Just _ -> pure result+              Nothing -> do+                _ <- done $ mb x+                pure result+          ) {-# INLINE resourceToIO #-} 
test/BracketSpec.hs view
@@ -1,5 +1,6 @@ module BracketSpec where +import Control.Monad import Polysemy import Polysemy.Error import Polysemy.Output@@ -7,137 +8,211 @@ import Polysemy.State import Polysemy.Trace import Test.Hspec+import Unsafe.Coerce  ++spec :: Spec+spec = parallel $ do+  testAllThree "persist state and call the finalizer"+      (\(ts, (s, e)) -> do+        s `shouldBe` "finalized"+        e `shouldBe` Left ()+        ts `shouldBe` ["allocated", "starting block"]+      ) $ do+    bracket+      (put "allocated" >> pure ())+      (\() -> do+        get >>= trace+        put "finalized"+      )+      (\() -> do+        get >>= trace+        put "starting block"+        _ <- throw ()+        put "don't get here"+      )++  testAllThree "persist state and call the finalizer with bracketOnError"+      (\(ts, (s, e)) -> do+        ts `shouldContain` ["allocated"]+        ts `shouldContain` ["starting block"]+        s `shouldBe` "finalized"+        e `shouldBe` Left ()+      ) $ do+    bracketOnError+      (put "allocated" >> pure ())+      (\() -> do+        get >>= trace+        put "finalized"+      )+      (\() -> do+        get >>= trace+        put "starting block"+        _ <- throw ()+        put "don't get here"+      )++  testAllThree "should not call the finalizer if there no error"+      (\(ts, (s, e)) -> do+        ts `shouldContain` ["allocated"]+        ts `shouldNotContain` ["starting block"]+        s `shouldBe` "don't get here"+        e `shouldBe` Right ()+      ) $ do+    bracketOnError+      (put "allocated" >> pure ())+      (\() -> do+        get >>= trace+        put "finalized"+      )+      (\() -> do+        get >>= trace+        put "starting block"+        put "don't get here"+      )++  testAllThree "should call the finalizer on Error"+      (\(ts, (s, e)) -> do+        ts `shouldContain` ["beginning transaction"]+        ts `shouldContain` ["rolling back transaction"]+        s `shouldBe` ""+        e `shouldBe` Left ()+      ) $ do+    withTransaction $ do+      void $ throw ()+      pure "hello"++  testTheIOTwo "io dispatched bracket"+      (\(ts, (s, e)) -> do+        ts `shouldContain` ["allocated"]+        ts `shouldContain` ["starting block"]+        s `shouldBe` "finalized"+        e `shouldBe` Left ()+      ) $ do+    bracket+      (put "allocated" >> pure ())+      (\() -> do+        get >>= trace+        put "finalized"+      )+      (\() -> do+        get >>= trace+        put "starting block"+        _ <- throw ()+        put "don't get here"+      )++  testTheIOTwo "should not lock when done recursively"+      (\(ts, (s, e)) -> do+        ts `shouldContain` [ "hello 1"+                           , "hello 2"+                           , "RUNNING"+                           , "goodbye 2"+                           ]+        s `shouldBe` "finished"+        e `shouldBe` Left ()+      ) $ do+    bracket+      (put "hello 1")+      (\() -> do+        get >>= trace+        put "finished"+      )+      (\() -> do+        get >>= trace+        void $+          bracket (put "hello 2")+                  (const $ do+                    get >>= trace+                    put "goodbye 2"+                  )+                  (const $ do+                    get >>= trace+                    put "RUNNING"+                    throw ()+                  )+        -- This doesn't run due to the thrown error above+        get >>= trace+        put "goodbye 1"+      )+++------------------------------------------------------------------------------++ runTest   :: Sem '[Error (), Resource, State [Char], Trace] a-  -> ([String], ([Char], Either () a))-runTest = run+  -> IO ([String], ([Char], Either () a))+runTest = pure+        . run         . runTraceList         . runState ""         . runResource         . runError @()  runTest2-  :: Sem '[Error (), Resource, State [Char], Trace, Embed IO] a+  :: Sem '[Error (), Resource, State [Char], Trace, Output String, Embed IO] a   -> IO ([String], ([Char], Either () a)) runTest2 = runM+         . ignoreOutput          . runTraceList          . runState ""          . resourceToIO          . runError @() +runTest3+  :: Sem '[Error (), Resource, State [Char], Trace, Output String, Embed IO, Final IO] a+  -> IO ([String], ([Char], Either () a))+runTest3 = runFinal+         . embedToFinal+         . outputToIOMonoid (:[])+         . traceToOutput+         . stateToIO ""+         . resourceToIOFinal+         . runError @() -spec :: Spec-spec = parallel $ do-  describe "pure bracket" $ do-    it "persist state and call the finalizer" $ do-      let (ts, (s, e)) = runTest $ do-            bracket-              (put "allocated" >> pure ())-              (\() -> do-                get >>= trace-                put "finalized"-              )-              (\() -> do-                get >>= trace-                put "starting block"-                _ <- throw ()-                put "don't get here"-              )-      ts `shouldContain` ["allocated"]-      ts `shouldContain` ["starting block"]-      s `shouldBe` "finalized"-      e `shouldBe` Left () -  describe "pure bracketOnError" $ do-    it "persist state and call the finalizer if there was an error" $ do-      let (ts, (s, e)) = runTest $ do-            bracketOnError-              (put "allocated" >> pure ())-              (\() -> do-                get >>= trace-                put "finalized"-              )-              (\() -> do-                get >>= trace-                put "starting block"-                _ <- throw ()-                put "don't get here"-              )-      ts `shouldContain` ["allocated"]-      ts `shouldContain` ["starting block"]-      s `shouldBe` "finalized"-      e `shouldBe` Left ()+testAllThree+    :: String+    -> (([String], ([Char], Either () a)) -> Expectation)+    -> (Sem '[Error (), Resource, State [Char], Trace] a)+    -> Spec+testAllThree name k m = do+  describe name $ do+    it "via runResource" $ do+      z <- runTest m+      k z+    -- NOTE(sandy): These unsafeCoerces are safe, because we're just weakening+    -- the end of the union+    it "via resourceToIO" $ do+      z <- runTest2 $ unsafeCoerce m+      k z+    it "via resourceToIOFinal" $ do+      z <- runTest3 $ unsafeCoerce m+      k z -    it "should not call the finalizer if there no error" $ do-      let (ts, (s, e)) = runTest $ do-            bracketOnError-              (put "allocated" >> pure ())-              (\() -> do-                get >>= trace-                put "finalized"-              )-              (\() -> do-                get >>= trace-                put "starting block"-                put "don't get here"-              )-      ts `shouldContain` ["allocated"]-      ts `shouldNotContain` ["starting block"]-      s `shouldBe` "don't get here"-      e `shouldBe` Right () +testTheIOTwo+    :: String+    -> (([String], ([Char], Either () a)) -> Expectation)+    -> (Sem '[Error (), Resource, State [Char], Trace, Output String, Embed IO] a)+    -> Spec+testTheIOTwo name k m = do+  describe name $ do+    it "via resourceToIO" $ do+      z <- runTest2 m+      k z+    -- NOTE(sandy): This unsafeCoerces are safe, because we're just weakening+    -- the end of the union+    it "via resourceToIOFinal" $ do+      z <- runTest3 $ unsafeCoerce m+      k z -  describe "io dispatched bracket" $ do-    it "persist state and call the finalizer" $ do-      (ts, (s, e)) <- runTest2 $ do-            bracket-              (put "allocated" >> pure ())-              (\() -> do-                get >>= trace-                put "finalized"-              )-              (\() -> do-                get >>= trace-                put "starting block"-                _ <- throw ()-                put "don't get here"-              )-      ts `shouldContain` ["allocated"]-      ts `shouldContain` ["starting block"]-      s `shouldBe` "finalized"-      e `shouldBe` Left () -    it "should not lock when done recursively" $ do-      (ts, (s, e)) <- runTest2 $ do-            bracket-              (put "hello 1")-              (\() -> do-                get >>= trace-                put "finished"-              )-              (\() -> do-                get >>= trace-                bracket (put "hello 2")-                        (const $ do-                          get >>= trace-                          put "goodbye 2"-                        )-                        (const $ do-                          get >>= trace-                          put "RUNNING"-                          throw ()-                        )-                -- This doesn't run due to the thrown error above-                get >>= trace-                put "goodbye 1"-              )-      ts `shouldContain` [ "hello 1"-                         , "hello 2"-                         , "RUNNING"-                         , "goodbye 2"-                         ]-      s `shouldBe` "finished"-      e `shouldBe` Left ()-+withTransaction :: (Member Resource r, Member Trace r) => Sem r a -> Sem r a+withTransaction m =+  bracketOnError+    (trace "beginning transaction")+    (const $ trace "rolling back transaction")+    (const $ m <* trace "committing transaction")