effectful 2.3.0.0 → 2.6.1.0
raw patch · 49 files changed
Files
- CHANGELOG.md +91/−0
- README.md +4/−4
- bench/Concurrency.hs +1/−1
- bench/Countdown.hs +63/−22
- bench/FileSizes.hs +27/−28
- bench/Main.hs +8/−0
- bench/Unlift.hs +2/−2
- effectful.cabal +34/−14
- src/Effectful/Concurrent.hs +14/−12
- src/Effectful/Concurrent/Async.hs +79/−43
- src/Effectful/Concurrent/Chan.hs +1/−1
- src/Effectful/Concurrent/Chan/Strict.hs +50/−0
- src/Effectful/Concurrent/Effect.hs +4/−4
- src/Effectful/Concurrent/MVar.hs +15/−2
- src/Effectful/Concurrent/MVar/Strict.hs +93/−88
- src/Effectful/Concurrent/MVar/Strict/Compat.hs +138/−0
- src/Effectful/Concurrent/QSem.hs +1/−1
- src/Effectful/Concurrent/QSemN.hs +1/−1
- src/Effectful/Concurrent/STM.hs +18/−3
- src/Effectful/Console/ByteString.hs +3/−3
- src/Effectful/Console/ByteString/Lazy.hs +3/−3
- src/Effectful/Console/Effect.hs +1/−1
- src/Effectful/Environment.hs +2/−2
- src/Effectful/FileSystem.hs +1/−1
- src/Effectful/FileSystem/Effect.hs +1/−1
- src/Effectful/FileSystem/IO.hs +1/−1
- src/Effectful/FileSystem/IO/ByteString.hs +8/−10
- src/Effectful/FileSystem/IO/ByteString/Builder.hs +4/−5
- src/Effectful/FileSystem/IO/ByteString/Lazy.hs +3/−3
- src/Effectful/FileSystem/IO/File.hs +1/−1
- src/Effectful/Prim/IORef.hs +79/−0
- src/Effectful/Prim/IORef/Strict.hs +66/−0
- src/Effectful/Process.hs +2/−2
- src/Effectful/Temporary.hs +2/−2
- src/Effectful/Timeout.hs +2/−2
- tests/AsyncTests.hs +2/−2
- tests/ConcurrencyTests.hs +37/−2
- tests/EnvTests.hs +38/−27
- tests/EnvironmentTests.hs +1/−1
- tests/ErrorTests.hs +10/−1
- tests/LabeledTests.hs +61/−0
- tests/Main.hs +2/−0
- tests/NonDetTests.hs +10/−7
- tests/PrimTests.hs +1/−1
- tests/ReaderTests.hs +15/−19
- tests/StateTests.hs +30/−13
- tests/TimeoutTests.hs +1/−1
- tests/UnliftTests.hs +84/−14
- tests/Utils.hs +10/−3
CHANGELOG.md view
@@ -1,3 +1,94 @@+# effectful-2.6.1.0 (2025-08-30)+* Add `MonadError`, `MonadReader`, `MonadState` and `MonadWriter` instances for+ `Eff` for compatibility with existing code.+* Re-export `writeTMVar` from `stm-2.5.1.0` in `Effectful.Concurrent.STM`.+* Add `cancelMany` to `Effectful.Concurrent.Async`.++# effectful-core-2.6.0.0 (2025-06-13)+* Adjust `generalBracket` with `base >= 4.21` to make use of the new exception+ annotation mechanism.+* Add `withException` to `Effectful.Exception`.+* Deprecate `Effectful.Reader.Dynamic.withReader` as it doesn't work correctly+ for all potential interpreters.+* Re-export `ThreadId` from `Effectful.Concurrent` for convenience.+* **Breaking changes**:+ - Change the order of type parameters in `raise` for better usability.+ - `Effectful.Error.Static.ErrorWrapper` is no longer caught by `catchSync`.+ - Remove deprecated function `Effectful.withConcEffToIO`.++# effectful-2.5.1.0 (2024-11-27)+* Add `passthrough` to `Effectful.Dispatch.Dynamic` for passing operations to+ the upstream handler within `interpose` and `impose` without having to fully+ pattern match on them.+* **Bugfixes**:+ - Fix a potential space leak related to `HasCallStack` quirks (see+ https://gitlab.haskell.org/ghc/ghc/-/issues/25520 for more information).++# effectful-2.5.0.0 (2024-10-23)+* Add `plusEff` (specialized version of `<|>`) to `Effectful.NonDet` and make+ `emptyEff` and `sumEff` generate better call stacks.+* Explicitly define `setByteArray#` and `setOffAddr#` in the `Prim` instance of+ `Ref` for `primitive` < 0.9.0.0.+* **Bugfixes**:+ - `OnEmptyRollback` strategy of the `NonDet` effect is no longer broken.+* **Breaking changes**:+ - Remove `restoreEnv` function from `Effectful.Dispatch.Static.Primitive`+ since it was broken.+ - Base `Effectful.Exception` on `Control.Exception` instead of the+ `safe-exceptions` library for consistency with provided `MonadThrow` and+ `MonadCatch` instances.++# effectful-2.4.0.0 (2024-10-08)+* Add utility functions for handling effects that take the effect handler as the+ last parameter to `Effectful.Dispatch.Dynamic`.+* Add utility functions for handling first order effects to+ `Effectful.Dispatch.Dynamic`.+* Improve `Effectful.Labeled`, add `Effectful.Labeled.Error`,+ `Effectful.Labeled.Reader`, `Effectful.Labeled.State` and+ `Effectful.Labeled.Writer`.+* Add `throwErrorWith` and `throwError_` to `Effectful.Error.Static` and+ `Effectful.Error.Dynamic`.+* Add `HasCallStack` constraints where appropriate for better debugging+ experience.+* Add a `SeqForkUnlift` strategy to support running unlifting functions outside+ of the scope of effects they capture.+* Add `Effectful.Exception` with appropriate re-exports from the+ `safe-exceptions` library.+* Add `Effectful.Concurrent.Chan.Strict`.+* Add `Effectful.Prim.IORef` and `Effectful.Prim.IORef.Strict`.+* **Bugfixes**:+ - Ensure that a `LocalEnv` is only used in a thread it belongs to.+ - Properly roll back changes made to the environment when `OnEmptyRollback`+ policy for the `NonDet` effect is selected.+ - Fix a bug in `stateM` and `modifyM` of thread local `State` effect that+ might've caused dropped state updates+ ([#237](https://github.com/haskell-effectful/effectful/issues/237)).+* **Breaking changes**:+ - `localSeqLend`, `localLend`, `localSeqBorrow` and `localBorrow` now take a+ list of effects instead of a single one.+ - `Effectful.Error.Static.throwError` now requires the error type to have a+ `Show` constraint. If this is not the case for some of your error types, use+ `throwError_` for them.+ - `ThrowError` operation from the dynamic version of the `Error` effect was+ replaced with `ThrowErrorWith`.+ - `stateEnv` and `modifyEnv` now take pure modification functions. If you rely+ on their old forms, switch to a combination of `getEnv` and `putEnv`.+ - `runStateMVar`, `evalStateMVar` and `execStateMVar` now take a strict+ `MVar'` from the `strict-mutable-base` package.+ - `Effectful.Concurrent.MVar.Strict` is now a lifted version of+ `Control.Concurrent.MVar.Strict` from `strict-mutable-base`. The original+ module was renamed to `Effectful.Concurrent.MVar.Strict.Compat` and+ deprecated.++# effectful-2.3.1.0 (2024-06-07)+* Drop support for GHC 8.8.+* Remove inaccurate information from the `Show` instance of `ErrorWrapper`.+* Add `Effectful.Provider.List`, generalization of `Effectful.Provider`.+* Respect `withFrozenCallStack` used by callers of `send`.+* Support exchange of effects between the environment of the handler and the+ local one via `localSeqLend`, `localLend`, `localSeqBorrow` and `localBorrow`+ from `Effectful.Dispatch.Dynamic`.+ # effectful-2.3.0.0 (2023-09-13) * Deprecate `withConcEffToIO`. * Make `withEffToIO` take an explicit unlifting strategy for the sake of
README.md view
@@ -1,13 +1,12 @@ # effectful -[](https://github.com/haskell-effectful/effectful/actions?query=branch%3Amaster)+[](https://github.com/haskell-effectful/effectful/actions/workflows/haskell-ci.yml) [](https://hackage.haskell.org/package/effectful)-[](https://packdeps.haskellers.com/feed?needle=andrzej@rybczak.net) [](https://www.stackage.org/lts/package/effectful) [](https://www.stackage.org/nightly/package/effectful) -<img src="https://user-images.githubusercontent.com/387658/127747903-f728437f-2ee4-47b8-9f0c-5102fd44c8e4.png" width="128">+<img src="https://raw.githubusercontent.com/haskell-effectful/effectful/master/logo.svg" width="150"> An easy to use, fast extensible effects library with seamless integration with the existing Haskell ecosystem.@@ -161,7 +160,8 @@ For the examples see the *Introduction* sections of [`Effectful.Dispatch.Dynamic`](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Dynamic.html) and-[`Effectful.Dispatch.Static`](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Static.html).+[`Effectful.Dispatch.Static`](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Static.html)+(when in doubt, start with dynamic dispatch). ## Acknowledgements
bench/Concurrency.hs view
@@ -11,9 +11,9 @@ import Control.Concurrent.Async import Control.Monad-import qualified Effectful.Concurrent.Async as A import Effectful+import Effectful.Concurrent.Async qualified as A import Effectful.Dispatch.Dynamic import Utils
bench/Countdown.hs view
@@ -6,51 +6,51 @@ -- eff #ifdef VERSION_eff-import qualified Control.Effect as L+import Control.Effect qualified as L #endif -- cleff #ifdef VERSION_cleff-import qualified Cleff as C-import qualified Cleff.Reader as C-import qualified Cleff.State as C+import Cleff qualified as C+import Cleff.Reader qualified as C+import Cleff.State qualified as C #endif -- effectful-import qualified Effectful as E-import qualified Effectful.Dispatch.Dynamic as E-import qualified Effectful.Labeled as E-import qualified Effectful.Reader.Static as E-import qualified Effectful.State.Dynamic as ED-import qualified Effectful.State.Static.Local as EL-import qualified Effectful.State.Static.Shared as ES+import Effectful qualified as E+import Effectful.Dispatch.Dynamic qualified as E+import Effectful.Labeled qualified as E+import Effectful.Reader.Static qualified as E+import Effectful.State.Dynamic qualified as ED+import Effectful.State.Static.Local qualified as EL+import Effectful.State.Static.Shared qualified as ES -- freer-simple #ifdef VERSION_freer_simple-import qualified Control.Monad.Freer as FS-import qualified Control.Monad.Freer.Reader as FS-import qualified Control.Monad.Freer.State as FS+import Control.Monad.Freer qualified as FS+import Control.Monad.Freer.Reader qualified as FS+import Control.Monad.Freer.State qualified as FS #endif -- fused-effects #ifdef VERSION_fused_effects-import qualified Control.Algebra as FE-import qualified Control.Carrier.Reader as FE-import qualified Control.Carrier.State.Strict as FE+import Control.Algebra qualified as FE+import Control.Carrier.Reader qualified as FE+import Control.Carrier.State.Strict qualified as FE #endif -- mtl #ifdef VERSION_mtl-import qualified Control.Monad.Reader as M-import qualified Control.Monad.State as M+import Control.Monad.Reader qualified as M+import Control.Monad.State qualified as M import Data.Functor.Identity #endif -- polysemy #ifdef VERSION_polysemy-import qualified Polysemy as P-import qualified Polysemy.Reader as P-import qualified Polysemy.State as P+import Polysemy qualified as P+import Polysemy.Reader qualified as P+import Polysemy.State qualified as P #endif ----------------------------------------@@ -282,6 +282,47 @@ . runDoubleStateShared n . runR . runR . runR . runR . runR $ programEffectfulDynamic+ where+ runR = E.runReader ()++----------------------------------------+-- effectful (labeled-dynamic-send)++programEffectfulLabeledDynamicSend+ :: E.Labeled "s" (ED.State Integer) E.:> es+ => E.Eff es Integer+programEffectfulLabeledDynamicSend = do+ n <- E.send . E.Labeled @"s" $ ED.Get @Integer+ if n <= 0+ then pure n+ else do+ E.send . E.Labeled @"s" $ ED.Put (n - 1)+ programEffectfulLabeledDynamicSend+{-# NOINLINE programEffectfulLabeledDynamicSend #-}++countdownEffectfulLabeledDynSendLocal :: Integer -> (Integer, Integer)+countdownEffectfulLabeledDynSendLocal n =+ E.runPureEff . E.runLabeled @"s" (ED.runStateLocal n) $ programEffectfulLabeledDynamicSend++countdownEffectfulLabeledDynSendShared :: Integer -> (Integer, Integer)+countdownEffectfulLabeledDynSendShared n =+ E.runPureEff . E.runLabeled @"s" (ED.runStateShared n) $ programEffectfulLabeledDynamicSend++countdownEffectfulLabeledDynSendLocalDeep :: Integer -> (Integer, Integer)+countdownEffectfulLabeledDynSendLocalDeep n = E.runPureEff+ . runR . runR . runR . runR . runR+ . E.runLabeled @"s" (ED.runStateLocal n)+ . runR . runR . runR . runR . runR+ $ programEffectfulLabeledDynamicSend+ where+ runR = E.runReader ()++countdownEffectfulLabeledDynSendSharedDeep :: Integer -> (Integer, Integer)+countdownEffectfulLabeledDynSendSharedDeep n = E.runPureEff+ . runR . runR . runR . runR . runR+ . E.runLabeled @"s" (ED.runStateShared n)+ . runR . runR . runR . runR . runR+ $ programEffectfulLabeledDynamicSend where runR = E.runReader ()
bench/FileSizes.hs view
@@ -1,59 +1,58 @@ {-# LANGUAGE BlockArguments #-} {-# LANGUAGE CPP #-}-{-# LANGUAGE UndecidableInstances #-} module FileSizes where import Control.Exception import Control.Monad.IO.Class import Data.IORef import Data.Text (Text)+import Data.Text qualified as T import System.Posix-import qualified Data.Text as T -- effectful-import qualified Effectful as E-import qualified Effectful.Dispatch.Dynamic as E-import qualified Effectful.Reader.Static as E-import qualified Effectful.State.Static.Local as E+import Effectful qualified as E+import Effectful.Dispatch.Dynamic qualified as E+import Effectful.Reader.Static qualified as E+import Effectful.State.Static.Local qualified as E -- eff #ifdef VERSION_eff-import qualified Control.Effect as L+import Control.Effect qualified as L #endif -- cleff #ifdef VERSION_cleff-import qualified Cleff as C-import qualified Cleff.Reader as C-import qualified Cleff.State as C+import Cleff qualified as C+import Cleff.Reader qualified as C+import Cleff.State qualified as C #endif -- freer-simple #ifdef VERSION_freer_simple-import qualified Control.Monad.Freer as FS-import qualified Control.Monad.Freer.Reader as FS-import qualified Control.Monad.Freer.State as FS+import Control.Monad.Freer qualified as FS+import Control.Monad.Freer.Reader qualified as FS+import Control.Monad.Freer.State qualified as FS #endif -- fused-effects #ifdef VERSION_fused_effects-import qualified Control.Algebra as FE-import qualified Control.Effect.Sum as FE-import qualified Control.Carrier.Reader as FE-import qualified Control.Carrier.State.Strict as FE+import Control.Algebra qualified as FE+import Control.Effect.Sum qualified as FE+import Control.Carrier.Reader qualified as FE+import Control.Carrier.State.Strict qualified as FE #endif -- mtl #ifdef VERSION_mtl-import qualified Control.Monad.State as M-import qualified Control.Monad.Reader as M+import Control.Monad.State qualified as M+import Control.Monad.Reader qualified as M #endif -- polysemy #ifdef VERSION_polysemy-import qualified Polysemy as P-import qualified Polysemy.Reader as P-import qualified Polysemy.State as P+import Polysemy qualified as P+import Polysemy.Reader qualified as P+import Polysemy.State qualified as P #endif tryGetFileSize :: FilePath -> IO (Maybe Int)@@ -102,7 +101,7 @@ effectful_tryFileSize = E.send . Effectful_tryFileSize effectful_runFile :: E.IOE E.:> es => E.Eff (Effectful_File : es) a -> E.Eff es a-effectful_runFile = E.interpret \_ -> \case+effectful_runFile = E.interpret_ \case Effectful_tryFileSize path -> liftIO $ tryGetFileSize path data Effectful_Logging :: E.Effect where@@ -116,7 +115,7 @@ effectful_runLogging :: E.Eff (Effectful_Logging : es) a -> E.Eff es (a, [Text])-effectful_runLogging = E.reinterpret (E.runState []) \_ -> \case+effectful_runLogging = E.reinterpret_ (E.runState []) \case Effectful_logMsg msg -> E.modify (msg :) ----------@@ -363,7 +362,7 @@ fe_tryFileSize = FE.send . FE_tryFileSize newtype FE_FileC m a = FE_FileC { fe_runFileC :: m a }- deriving (Applicative, Functor, Monad, MonadIO)+ deriving newtype (Applicative, Functor, Monad, MonadIO) instance ( MonadIO m@@ -380,7 +379,7 @@ fe_logMsg = FE.send . FE_logMsg . T.pack newtype FE_LoggingC m a = FE_LoggingC { fe_runLoggingC :: FE.StateC [Text] m a }- deriving (Applicative, Functor, Monad)+ deriving newtype (Applicative, Functor, Monad) instance ( FE.Algebra sig m@@ -440,7 +439,7 @@ mtl_tryFileSize :: FilePath -> m (Maybe Int) newtype FileT m a = FileT { runFileT :: m a }- deriving (Functor, Applicative, Monad, MonadIO)+ deriving newtype (Functor, Applicative, Monad, MonadIO) instance M.MonadTrans FileT where lift = FileT@@ -459,7 +458,7 @@ mtl_logMsg :: String -> m () newtype LoggingT m a = LoggingT (M.StateT [Text] m a)- deriving (Functor, Applicative, Monad, MonadIO, M.MonadTrans)+ deriving newtype (Functor, Applicative, Monad, MonadIO, M.MonadTrans) instance {-# OVERLAPPABLE #-} ( MonadLog m
bench/Main.hs view
@@ -64,6 +64,10 @@ [ bench "shallow" $ nf countdownEffectfulDynLocal n , bench "deep" $ nf countdownEffectfulDynLocalDeep n ]+ , bgroup "effectful (local/dynamic/labeled/send)"+ [ bench "shallow" $ nf countdownEffectfulLabeledDynSendLocal n+ , bench "deep" $ nf countdownEffectfulLabeledDynSendLocalDeep n+ ] , bgroup "effectful (shared/static)" [ bench "shallow" $ nf countdownEffectfulShared n , bench "deep" $ nf countdownEffectfulSharedDeep n@@ -71,6 +75,10 @@ , bgroup "effectful (shared/dynamic)" [ bench "shallow" $ nf countdownEffectfulDynShared n , bench "deep" $ nf countdownEffectfulDynSharedDeep n+ ]+ , bgroup "effectful (shared/dynamic/labeled/send)"+ [ bench "shallow" $ nf countdownEffectfulLabeledDynSendShared n+ , bench "deep" $ nf countdownEffectfulLabeledDynSendSharedDeep n ] #ifdef VERSION_cleff , bgroup "cleff (local)"
bench/Unlift.hs view
@@ -9,12 +9,12 @@ import Test.Tasty.Bench #endif -import qualified UnliftIO as U+import UnliftIO qualified as U import Effectful+import Effectful.Concurrent.Async qualified as A import Effectful.State.Dynamic import Utils-import qualified Effectful.Concurrent.Async as A unliftBenchmark :: Benchmark unliftBenchmark = bgroup "unlifting"
effectful.cabal view
@@ -1,7 +1,7 @@-cabal-version: 2.4+cabal-version: 3.0 build-type: Simple name: effectful-version: 2.3.0.0+version: 2.6.1.0 license: BSD-3-Clause license-file: LICENSE category: Control@@ -22,8 +22,7 @@ CHANGELOG.md README.md -tested-with: GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.7 || ==9.6.2- || ==9.8.1+tested-with: GHC == { 8.10.7, 9.0.2, 9.2.8, 9.4.8, 9.6.7, 9.8.4, 9.10.2, 9.12.2, 9.14.1 } bug-reports: https://github.com/haskell-effectful/effectful/issues source-repository head@@ -35,7 +34,11 @@ default: False common language- ghc-options: -Wall -Wcompat -Wno-unticked-promoted-constructors+ ghc-options: -Wall+ -Wcompat+ -Wno-unticked-promoted-constructors+ -Wmissing-deriving-strategies+ -Werror=prepositive-qualified-module default-language: Haskell2010 @@ -44,13 +47,16 @@ DataKinds DeriveFunctor DeriveGeneric+ DerivingStrategies FlexibleContexts FlexibleInstances GADTs GeneralizedNewtypeDeriving+ ImportQualifiedPost LambdaCase MultiParamTypeClasses NoStarIsType+ PolyKinds RankNTypes RecordWildCards RoleAnnotations@@ -60,19 +66,20 @@ TypeApplications TypeFamilies TypeOperators+ UndecidableInstances library import: language - build-depends: base >= 4.13 && < 5- , async >= 2.2.2+ build-depends: base >= 4.14 && < 5+ , async >= 2.2.5 , bytestring >= 0.10 , directory >= 1.3.2- , effectful-core >= 2.3.0.0 && < 2.3.1.0+ , effectful-core >= 2.6.1.0 && < 2.6.2.0 , process >= 1.6.9-+ , strict-mutable-base >= 1.1.0.0 , time >= 1.9.2- , stm >= 2.5.0.0+ , stm >= 2.5.1.0 , unliftio >= 0.2.20 hs-source-dirs: src@@ -80,8 +87,10 @@ exposed-modules: Effectful.Concurrent Effectful.Concurrent.Async Effectful.Concurrent.Chan+ Effectful.Concurrent.Chan.Strict Effectful.Concurrent.MVar Effectful.Concurrent.MVar.Strict+ Effectful.Concurrent.MVar.Strict.Compat Effectful.Concurrent.STM Effectful.Concurrent.QSem Effectful.Concurrent.QSemN@@ -94,6 +103,8 @@ Effectful.FileSystem.IO.ByteString.Builder Effectful.FileSystem.IO.ByteString.Lazy Effectful.FileSystem.IO.File+ Effectful.Prim.IORef+ Effectful.Prim.IORef.Strict Effectful.Process Effectful.Temporary Effectful.Timeout@@ -107,11 +118,17 @@ , Effectful.Dispatch.Static , Effectful.Error.Static , Effectful.Error.Dynamic+ , Effectful.Exception , Effectful.Fail , Effectful.Labeled+ , Effectful.Labeled.Error+ , Effectful.Labeled.Reader+ , Effectful.Labeled.State+ , Effectful.Labeled.Writer , Effectful.NonDet , Effectful.Prim , Effectful.Provider+ , Effectful.Provider.List , Effectful.Reader.Dynamic , Effectful.Reader.Static , Effectful.State.Dynamic@@ -133,6 +150,8 @@ , exceptions , lifted-base , primitive+ , safe-exceptions+ , strict-mutable-base , tasty , tasty-hunit , unliftio@@ -147,6 +166,7 @@ EnvTests EnvironmentTests ErrorTests+ LabeledTests NonDetTests PrimTests ReaderTests@@ -169,11 +189,11 @@ if impl(ghc < 9.9) build-depends: freer-simple >= 1.2.1.2 - if impl(ghc < 9.9)- build-depends: fused-effects >= 1.1.2.2+ if impl(ghc < 9.15)+ build-depends: fused-effects >= 1.1.2.3 - if impl(ghc < 9.7)- build-depends: polysemy >= 1.9.1.0+ if impl(ghc < 9.15)+ build-depends: polysemy >= 1.9.2.0 build-depends: base , async
src/Effectful/Concurrent.hs view
@@ -20,6 +20,7 @@ , runConcurrent -- * Basic concurrency operations+ , C.ThreadId , myThreadId , forkIO , forkFinally@@ -59,13 +60,13 @@ , C.rtsSupportsBoundThreads ) where +import Control.Concurrent qualified as C import Control.Exception (Exception, SomeException) import Data.Bifunctor (second)+import GHC.Conc qualified as GHC import System.Mem.Weak (Weak) import System.Posix.Types (Fd) import UnliftIO.STM (STM)-import qualified Control.Concurrent as C-import qualified GHC.Conc as GHC import Effectful import Effectful.Concurrent.Effect@@ -81,14 +82,14 @@ myThreadId = unsafeEff_ C.myThreadId -- | Lifted 'C.forkIO'.-forkIO :: Concurrent :> es => Eff es () -> Eff es C.ThreadId+forkIO :: (HasCallStack, Concurrent :> es) => Eff es () -> Eff es C.ThreadId forkIO k = unsafeEff $ \es -> do esF <- cloneEnv es C.forkIO $ unEff k esF -- | Lifted 'C.forkFinally'. forkFinally- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => Eff es a -> (Either SomeException a -> Eff es ()) -> Eff es C.ThreadId@@ -98,7 +99,7 @@ -- | Lifted 'C.forkIOWithUnmask'. forkIOWithUnmask- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => ((forall a. Eff es a -> Eff es a) -> Eff es ()) -> Eff es C.ThreadId forkIOWithUnmask = liftForkWithUnmask C.forkIOWithUnmask@@ -115,14 +116,14 @@ -- Threads with affinity -- | Lifted 'C.forkOn'.-forkOn :: Concurrent :> es => Int -> Eff es () -> Eff es C.ThreadId+forkOn :: (HasCallStack, Concurrent :> es) => Int -> Eff es () -> Eff es C.ThreadId forkOn n k = unsafeEff $ \es -> do esF <- cloneEnv es C.forkOn n (unEff k esF) -- | Lifted 'C.forkOnWithUnmask'. forkOnWithUnmask- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => Int -> ((forall a. Eff es a -> Eff es a) -> Eff es ()) -> Eff es C.ThreadId@@ -180,14 +181,14 @@ -- Bound threads -- | Lifted 'C.forkOS'.-forkOS :: Concurrent :> es => Eff es () -> Eff es C.ThreadId+forkOS :: (HasCallStack, Concurrent :> es) => Eff es () -> Eff es C.ThreadId forkOS k = unsafeEff $ \es -> do esF <- cloneEnv es C.forkOS $ unEff k esF -- | Lifted 'E.forkOSWithUnmask'. forkOSWithUnmask- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => ((forall a. Eff es a -> Eff es a) -> Eff es ()) -> Eff es C.ThreadId forkOSWithUnmask = liftForkWithUnmask C.forkOSWithUnmask@@ -197,13 +198,13 @@ isCurrentThreadBound = unsafeEff_ C.isCurrentThreadBound -- | Lifted 'C.runInBoundThread'.-runInBoundThread :: Concurrent :> es => Eff es a -> Eff es a+runInBoundThread :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es a runInBoundThread k = unsafeEff $ \es -> do esF <- cloneEnv es C.runInBoundThread $ unEff k esF -- | Lifted 'C.runInUnboundThread'.-runInUnboundThread :: Concurrent :> es => Eff es a -> Eff es a+runInUnboundThread :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es a runInUnboundThread k = unsafeEff $ \es -> do esF <- cloneEnv es C.runInUnboundThread $ unEff k esF@@ -219,7 +220,8 @@ -- Helpers liftForkWithUnmask- :: (((forall c. IO c -> IO c) -> IO a) -> IO C.ThreadId)+ :: HasCallStack+ => (((forall c. IO c -> IO c) -> IO a) -> IO C.ThreadId) -> ((forall c. Eff es c -> Eff es c) -> Eff es a) -> Eff es C.ThreadId liftForkWithUnmask fork action = unsafeEff $ \es -> do
src/Effectful/Concurrent/Async.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE UndecidableInstances #-} -- | Lifted "Control.Concurrent.Async". module Effectful.Concurrent.Async ( -- * Effect@@ -18,7 +17,8 @@ -- ** Querying 'Async's , wait, poll, waitCatch, A.asyncThreadId- , cancel, uninterruptibleCancel, cancelWith, A.AsyncCancelled(..)+ , cancel, cancelMany, uninterruptibleCancel, cancelWith+ , A.AsyncCancelled(..) , A.compareAsyncs -- ** High-level utilities@@ -80,9 +80,9 @@ import Control.Exception (Exception, SomeException) import Control.Monad (forever) import Data.Kind (Type)-import qualified Control.Concurrent.Async as A-import qualified UnliftIO.Async as U-import qualified UnliftIO.Internals.Async as I+import Control.Concurrent.Async qualified as A+import UnliftIO.Async qualified as U+import UnliftIO.Internals.Async qualified as I import Effectful import Effectful.Concurrent.Effect@@ -91,27 +91,27 @@ import Effectful.Dispatch.Static.Unsafe -- | Lifted 'A.async'.-async :: Concurrent :> es => Eff es a -> Eff es (Async a)+async :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es (Async a) async = liftAsync A.async -- | Lifted 'A.asyncBound'.-asyncBound :: Concurrent :> es => Eff es a -> Eff es (Async a)+asyncBound :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es (Async a) asyncBound = liftAsync A.asyncBound -- | Lifted 'A.asyncOn'.-asyncOn :: Concurrent :> es => Int -> Eff es a -> Eff es (Async a)+asyncOn :: (HasCallStack, Concurrent :> es) => Int -> Eff es a -> Eff es (Async a) asyncOn cpu = liftAsync (A.asyncOn cpu) -- | Lifted 'A.asyncWithUnmask'. asyncWithUnmask- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => ((forall b. Eff es b -> Eff es b) -> Eff es a) -> Eff es (Async a) asyncWithUnmask = liftAsyncWithUnmask A.asyncWithUnmask -- | Lifted 'A.asyncOnWithUnmask'. asyncOnWithUnmask- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => Int -> ((forall b. Eff es b -> Eff es b) -> Eff es a) -> Eff es (Async a)@@ -119,7 +119,7 @@ -- | Lifted 'A.withAsync'. withAsync- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => Eff es a -> (Async a -> Eff es b) -> Eff es b@@ -127,7 +127,7 @@ -- | Lifted 'A.withAsyncBound'. withAsyncBound- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => Eff es a -> (Async a -> Eff es b) -> Eff es b@@ -135,7 +135,7 @@ -- | Lifted 'A.withAsyncOn'. withAsyncOn- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => Int -> Eff es a -> (Async a -> Eff es b)@@ -144,7 +144,7 @@ -- | Lifted 'A.withAsyncWithUnmask'. withAsyncWithUnmask- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => ((forall c. Eff es c -> Eff es c) -> Eff es a) -> (Async a -> Eff es b) -> Eff es b@@ -152,7 +152,7 @@ -- | Lifted 'A.withAsyncOnWithUnmask'. withAsyncOnWithUnmask- :: Concurrent :> es+ :: (HasCallStack, Concurrent :> es) => Int -> ((forall c. Eff es c -> Eff es c) -> Eff es a) -> (Async a -> Eff es b)@@ -174,6 +174,12 @@ cancel :: Concurrent :> es => Async a -> Eff es () cancel = unsafeEff_ . A.cancel +-- | Lifted 'A.cancelMany'.+--+-- @since 2.6.1.0+cancelMany :: Concurrent :> es => [Async a] -> Eff es ()+cancelMany = unsafeEff_ . A.cancelMany+ -- | Lifted 'A.cancelWith'. cancelWith :: (Exception e, Concurrent :> es) => Async a -> e -> Eff es () cancelWith a = unsafeEff_ . A.cancelWith a@@ -268,22 +274,22 @@ link2Only f a b = unsafeEff_ $ A.link2Only f a b -- | Lifted 'A.race'.-race :: Concurrent :> es => Eff es a -> Eff es b -> Eff es (Either a b)+race :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es b -> Eff es (Either a b) race ma mb = unsafeEff $ \es -> do A.race (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es) -- | Lifted 'A.race_'.-race_ :: Concurrent :> es => Eff es a -> Eff es b -> Eff es ()+race_ :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es b -> Eff es () race_ ma mb = unsafeEff $ \es -> do A.race_ (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es) -- | Lifted 'A.concurrently'.-concurrently :: Concurrent :> es => Eff es a -> Eff es b -> Eff es (a, b)+concurrently :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es b -> Eff es (a, b) concurrently ma mb = unsafeEff $ \es -> do A.concurrently (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es) -- | Lifted 'A.concurrently_'.-concurrently_ :: Concurrent :> es => Eff es a -> Eff es b -> Eff es ()+concurrently_ :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es b -> Eff es () concurrently_ ma mb = unsafeEff $ \es -> do A.concurrently_ (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es) @@ -293,7 +299,7 @@ -- | Lifted 'A.mapConcurrently'. mapConcurrently- :: (Traversable f, Concurrent :> es)+ :: (HasCallStack, Traversable f, Concurrent :> es) => (a -> Eff es b) -> f a -> Eff es (f b)@@ -302,7 +308,7 @@ -- | Lifted 'A.mapConcurrently_'. mapConcurrently_- :: (Foldable f, Concurrent :> es)+ :: (HasCallStack, Foldable f, Concurrent :> es) => (a -> Eff es b) -> f a -> Eff es ()@@ -311,7 +317,7 @@ -- | Lifted 'A.forConcurrently'. forConcurrently- :: (Traversable f, Concurrent :> es)+ :: (HasCallStack, Traversable f, Concurrent :> es) => f a -> (a -> Eff es b) -> Eff es (f b)@@ -320,7 +326,7 @@ -- | Lifted 'A.forConcurrently_'. forConcurrently_- :: (Foldable f, Concurrent :> es)+ :: (HasCallStack, Foldable f, Concurrent :> es) => f a -> (a -> Eff es b) -> Eff es ()@@ -328,12 +334,20 @@ U.forConcurrently_ t (\a -> unEff (f a) =<< cloneEnv es) -- | Lifted 'A.replicateConcurrently'.-replicateConcurrently :: Concurrent :> es => Int -> Eff es a -> Eff es [a]+replicateConcurrently+ :: (HasCallStack, Concurrent :> es)+ => Int+ -> Eff es a+ -> Eff es [a] replicateConcurrently n f = unsafeEff $ \es -> do U.replicateConcurrently n (unEff f =<< cloneEnv es) -- | Lifted 'A.replicateConcurrently_'.-replicateConcurrently_ :: Concurrent :> es => Int -> Eff es a -> Eff es ()+replicateConcurrently_+ :: (HasCallStack, Concurrent :> es)+ => Int+ -> Eff es a+ -> Eff es () replicateConcurrently_ n f = unsafeEff $ \es -> do U.replicateConcurrently_ n (unEff f =<< cloneEnv es) @@ -342,7 +356,7 @@ -- | Lifted 'U.pooledMapConcurrentlyN'. pooledMapConcurrentlyN- :: (Concurrent :> es, Traversable t)+ :: (HasCallStack, Concurrent :> es, Traversable t) => Int -> (a -> Eff es b) -> t a@@ -352,7 +366,7 @@ -- | Lifted 'U.pooledMapConcurrently'. pooledMapConcurrently- :: (Concurrent :> es, Traversable t)+ :: (HasCallStack, Concurrent :> es, Traversable t) => (a -> Eff es b) -> t a -> Eff es (t b)@@ -361,7 +375,7 @@ -- | Lifted 'U.pooledMapConcurrentlyN'. pooledMapConcurrentlyN_- :: (Concurrent :> es, Foldable f)+ :: (HasCallStack, Concurrent :> es, Foldable f) => Int -> (a -> Eff es b) -> f a@@ -371,7 +385,7 @@ -- | Lifted 'U.pooledMapConcurrently_'. pooledMapConcurrently_- :: (Concurrent :> es, Foldable f)+ :: (HasCallStack, Concurrent :> es, Foldable f) => (a -> Eff es b) -> f a -> Eff es ()@@ -380,7 +394,7 @@ -- | Lifted 'U.pooledForConcurrentlyN'. pooledForConcurrentlyN- :: (Concurrent :> es, Traversable t)+ :: (HasCallStack, Concurrent :> es, Traversable t) => Int -> t a -> (a -> Eff es b)@@ -390,7 +404,7 @@ -- | Lifted 'U.pooledForConcurrently'. pooledForConcurrently- :: (Concurrent :> es, Traversable t)+ :: (HasCallStack, Concurrent :> es, Traversable t) => t a -> (a -> Eff es b) -> Eff es (t b)@@ -399,7 +413,7 @@ -- | Lifted 'U.pooledForConcurrentlyN'. pooledForConcurrentlyN_- :: (Concurrent :> es, Foldable f)+ :: (HasCallStack, Concurrent :> es, Foldable f) => Int -> f a -> (a -> Eff es b)@@ -409,7 +423,7 @@ -- | Lifted 'U.pooledForConcurrently_'. pooledForConcurrently_- :: (Concurrent :> es, Foldable f)+ :: (HasCallStack, Concurrent :> es, Foldable f) => f a -> (a -> Eff es b) -> Eff es ()@@ -417,22 +431,40 @@ U.pooledForConcurrently_ t (\a -> unEff (f a) =<< cloneEnv es) -- | Lifted 'U.pooledReplicateConcurrentlyN'.-pooledReplicateConcurrentlyN :: Concurrent :> es => Int -> Int -> Eff es a -> Eff es [a]+pooledReplicateConcurrentlyN+ :: (HasCallStack, Concurrent :> es)+ => Int+ -> Int+ -> Eff es a+ -> Eff es [a] pooledReplicateConcurrentlyN threads n f = unsafeEff $ \es -> do U.pooledReplicateConcurrentlyN threads n (unEff f =<< cloneEnv es) -- | Lifted 'U.pooledReplicateConcurrently'.-pooledReplicateConcurrently :: Concurrent :> es => Int -> Eff es a -> Eff es [a]+pooledReplicateConcurrently+ :: (HasCallStack, Concurrent :> es)+ => Int+ -> Eff es a+ -> Eff es [a] pooledReplicateConcurrently n f = unsafeEff $ \es -> do U.pooledReplicateConcurrently n (unEff f =<< cloneEnv es) -- | Lifted 'U.pooledReplicateConcurrentlyN_'.-pooledReplicateConcurrentlyN_ :: Concurrent :> es => Int -> Int -> Eff es a -> Eff es ()+pooledReplicateConcurrentlyN_+ :: (HasCallStack, Concurrent :> es)+ => Int+ -> Int+ -> Eff es a+ -> Eff es () pooledReplicateConcurrentlyN_ threads n f = unsafeEff $ \es -> do U.pooledReplicateConcurrentlyN_ threads n (unEff f =<< cloneEnv es) -- | Lifted 'U.pooledReplicateConcurrently_'.-pooledReplicateConcurrently_ :: Concurrent :> es => Int -> Eff es a -> Eff es ()+pooledReplicateConcurrently_+ :: (HasCallStack, Concurrent :> es)+ => Int+ -> Eff es a+ -> Eff es () pooledReplicateConcurrently_ n f = unsafeEff $ \es -> do U.pooledReplicateConcurrently_ n (unEff f =<< cloneEnv es) @@ -448,7 +480,7 @@ Alt :: Conc es a -> Conc es a -> Conc es a Empty :: Conc es a -deriving instance Functor (Conc es)+deriving stock instance Functor (Conc es) instance Applicative (Conc es) where pure = Pure@@ -472,7 +504,7 @@ conc = Action -- | Lifted 'U.runConc'.-runConc :: Concurrent :> es => Conc es a -> Eff es a+runConc :: (HasCallStack, Concurrent :> es) => Conc es a -> Eff es a runConc m = unsafeEff $ \es -> U.runConc (unliftConc es m) where unliftConc :: Env es -> Conc es a -> U.Conc IO a@@ -513,7 +545,8 @@ -- Helpers liftAsync- :: (IO a -> IO (Async a))+ :: HasCallStack+ => (IO a -> IO (Async a)) -> Eff es a -> Eff es (Async a) liftAsync fork action = unsafeEff $ \es -> do@@ -521,7 +554,8 @@ fork $ unEff action esA liftAsyncWithUnmask- :: (((forall b. IO b -> IO b) -> IO a) -> IO (Async a))+ :: HasCallStack+ => (((forall b. IO b -> IO b) -> IO a) -> IO (Async a)) -> ((forall b. Eff es b -> Eff es b) -> Eff es a) -> Eff es (Async a) liftAsyncWithUnmask fork action = unsafeEff $ \es -> do@@ -530,7 +564,8 @@ fork $ \unmask -> unEff (action $ reallyUnsafeLiftMapIO unmask) esA liftWithAsync- :: (IO a -> (Async a -> IO b) -> IO b)+ :: HasCallStack+ => (IO a -> (Async a -> IO b) -> IO b) -> Eff es a -> (Async a -> Eff es b) -> Eff es b@@ -540,7 +575,8 @@ (\a -> unEff (k a) es) liftWithAsyncWithUnmask- :: (((forall c. IO c -> IO c) -> IO a) -> (Async a -> IO b) -> IO b)+ :: HasCallStack+ => (((forall c. IO c -> IO c) -> IO a) -> (Async a -> IO b) -> IO b) -> ((forall c. Eff es c -> Eff es c) -> Eff es a) -> (Async a -> Eff es b) -> Eff es b
src/Effectful/Concurrent/Chan.hs view
@@ -17,7 +17,7 @@ ) where import Control.Concurrent.Chan (Chan)-import qualified Control.Concurrent.Chan as C+import Control.Concurrent.Chan qualified as C import Effectful import Effectful.Concurrent.Effect
+ src/Effectful/Concurrent/Chan/Strict.hs view
@@ -0,0 +1,50 @@+-- | Lifted "Control.Concurrent.Chan.Strict".+--+-- @since 2.4.0.0+module Effectful.Concurrent.Chan.Strict+ ( -- * Effect+ Concurrent++ -- ** Handlers+ , runConcurrent++ -- * Chan+ , Chan'+ , newChan'+ , writeChan'+ , readChan'+ , dupChan'+ , getChan'Contents+ , writeList2Chan'+ ) where++import Control.Concurrent.Chan.Strict (Chan')+import Control.Concurrent.Chan.Strict qualified as C++import Effectful+import Effectful.Concurrent.Effect+import Effectful.Dispatch.Static++-- | Lifted 'C.newChan''.+newChan' :: Concurrent :> es => Eff es (Chan' a)+newChan' = unsafeEff_ C.newChan'++-- | Lifted 'C.writeChan''.+writeChan' :: Concurrent :> es => Chan' a -> a -> Eff es ()+writeChan' c = unsafeEff_ . C.writeChan' c++-- | Lifted 'C.readChan''.+readChan' :: Concurrent :> es => Chan' a -> Eff es a+readChan' = unsafeEff_ . C.readChan'++-- | Lifted 'C.dupChan''.+dupChan' :: Concurrent :> es => Chan' a -> Eff es (Chan' a)+dupChan' = unsafeEff_ . C.dupChan'++-- | Lifted 'C.getChan'Contents'.+getChan'Contents :: Concurrent :> es => Chan' a -> Eff es [a]+getChan'Contents = unsafeEff_ . C.getChan'Contents++-- | Lifted 'C.writeList2Chan''.+writeList2Chan' :: Concurrent :> es => Chan' a -> [a] -> Eff es ()+writeList2Chan' c = unsafeEff_ . C.writeList2Chan' c
src/Effectful/Concurrent/Effect.hs view
@@ -17,7 +17,7 @@ -- it possible to escape the scope of any scoped effect operation. Consider the -- following: ----- >>> import qualified Effectful.Reader.Static as R+-- >>> import Effectful.Reader.Static qualified as R -- -- >>> printAsk msg = liftIO . putStrLn . (msg ++) . (": " ++) =<< R.ask --@@ -54,14 +54,14 @@ -- a mutable state. That's why statically dispatched @State@ and @Writer@ -- effects come in two flavors, local and shared: ----- >>> import qualified Effectful.State.Static.Local as SL+-- >>> import Effectful.State.Static.Local qualified as SL -- >>> :{ -- runEff . SL.execState "Hi" . runConcurrent $ do -- replicateConcurrently_ 3 $ SL.modify (++ "!") -- :} -- "Hi" ----- >>> import qualified Effectful.State.Static.Shared as SS+-- >>> import Effectful.State.Static.Shared qualified as SS -- >>> :{ -- runEff . SS.execState "Hi" . runConcurrent $ do -- replicateConcurrently_ 3 $ SS.modify (++ "!")@@ -78,7 +78,7 @@ data instance StaticRep Concurrent = Concurrent -- | Run the 'Concurrent' effect.-runConcurrent :: IOE :> es => Eff (Concurrent : es) a -> Eff es a+runConcurrent :: (HasCallStack, IOE :> es) => Eff (Concurrent : es) a -> Eff es a runConcurrent = evalStaticRep Concurrent -- $setup
src/Effectful/Concurrent/MVar.hs view
@@ -29,7 +29,7 @@ import System.Mem.Weak (Weak) import Control.Concurrent.MVar (MVar)-import qualified Control.Concurrent.MVar as M+import Control.Concurrent.MVar qualified as M import Effectful import Effectful.Concurrent.Effect@@ -81,34 +81,47 @@ withMVar :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b withMVar var f = reallyUnsafeUnliftIO $ \unlift -> do M.withMVar var $ unlift . f+{-# INLINE withMVar #-} -- | Lifted 'M.withMVarMasked'. withMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b withMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do M.withMVarMasked var $ unlift . f+{-# INLINE withMVarMasked #-} -- | Lifted 'M.modifyMVar_'. modifyMVar_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es () modifyMVar_ var f = reallyUnsafeUnliftIO $ \unlift -> do M.modifyMVar_ var $ unlift . f+{-# INLINE modifyMVar_ #-} -- | Lifted 'M.modifyMVar'. modifyMVar :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b modifyMVar var f = reallyUnsafeUnliftIO $ \unlift -> do M.modifyMVar var $ unlift . f+{-# INLINE modifyMVar #-} -- | Lifted 'M.modifyMVarMasked_'. modifyMVarMasked_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es () modifyMVarMasked_ var f = reallyUnsafeUnliftIO $ \unlift -> do M.modifyMVarMasked_ var $ unlift . f+{-# INLINE modifyMVarMasked_ #-} -- | Lifted 'M.modifyMVarMasked'. modifyMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b modifyMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do M.modifyMVarMasked var $ unlift . f+{-# INLINE modifyMVarMasked #-} -- | Lifted 'M.mkWeakMVar'.-mkWeakMVar :: Concurrent :> es => MVar a -> Eff es () -> Eff es (Weak (MVar a))+--+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes+-- to thread local data will not be visible outside of it.+mkWeakMVar+ :: (HasCallStack, Concurrent :> es)+ => MVar a+ -> Eff es ()+ -> Eff es (Weak (MVar a)) mkWeakMVar var f = unsafeEff $ \es -> do -- The finalizer can run at any point and in any thread. M.mkWeakMVar var . unEff f =<< cloneEnv es
src/Effectful/Concurrent/MVar/Strict.hs view
@@ -1,5 +1,6 @@--- | Lifted "Control.Concurrent.MVar" with operations that force values put--- inside an 'MVar' to WHNF.+-- | Lifted "Control.Concurrent.MVar.Strict".+--+-- @since 2.4.0.0 module Effectful.Concurrent.MVar.Strict ( -- * Effect Concurrent@@ -8,29 +9,29 @@ , runConcurrent -- * MVar- , MVar- , newEmptyMVar- , newMVar- , takeMVar- , putMVar- , readMVar- , swapMVar- , tryTakeMVar- , tryPutMVar- , isEmptyMVar- , withMVar- , withMVarMasked- , modifyMVar- , modifyMVar_- , modifyMVarMasked- , modifyMVarMasked_- , tryReadMVar- , mkWeakMVar+ , MVar'+ , newEmptyMVar'+ , newMVar'+ , takeMVar'+ , putMVar'+ , readMVar'+ , swapMVar'+ , tryTakeMVar'+ , tryPutMVar'+ , tryReadMVar'+ , isEmptyMVar'+ , withMVar'+ , withMVar'Masked+ , modifyMVar'+ , modifyMVar'_+ , modifyMVar'Masked+ , modifyMVar'Masked_+ , mkWeakMVar' ) where import System.Mem.Weak (Weak)-import Control.Concurrent.MVar (MVar)-import qualified Control.Concurrent.MVar as M+import Control.Concurrent.MVar.Strict (MVar')+import Control.Concurrent.MVar.Strict qualified as M import Effectful import Effectful.Concurrent.Effect@@ -38,86 +39,90 @@ import Effectful.Dispatch.Static.Primitive import Effectful.Dispatch.Static.Unsafe --- | Lifted 'M.newEmptyMVar'.-newEmptyMVar :: Concurrent :> es => Eff es (MVar a)-newEmptyMVar = unsafeEff_ M.newEmptyMVar+-- | Lifted 'M.newEmptyMVar''.+newEmptyMVar' :: Concurrent :> es => Eff es (MVar' a)+newEmptyMVar' = unsafeEff_ M.newEmptyMVar' --- | Lifted 'M.newMVar' that evaluates the value to WHNF.-newMVar :: Concurrent :> es => a -> Eff es (MVar a)-newMVar a = unsafeEff_ $ a `seq` M.newMVar a+-- | Lifted 'M.newMVar''.+newMVar' :: Concurrent :> es => a -> Eff es (MVar' a)+newMVar' = unsafeEff_ . M.newMVar' --- | Lifted 'M.takeMVar'.-takeMVar :: Concurrent :> es => MVar a -> Eff es a-takeMVar = unsafeEff_ . M.takeMVar+-- | Lifted 'M.takeMVar''.+takeMVar' :: Concurrent :> es => MVar' a -> Eff es a+takeMVar' = unsafeEff_ . M.takeMVar' --- | Lifted 'M.putMVar'.-putMVar :: Concurrent :> es => MVar a -> a -> Eff es ()-putMVar var a = unsafeEff_ $ a `seq` M.putMVar var a+-- | Lifted 'M.putMVar''.+putMVar' :: Concurrent :> es => MVar' a -> a -> Eff es ()+putMVar' var = unsafeEff_ . M.putMVar' var --- | Lifted 'M.readMVar'.-readMVar :: Concurrent :> es => MVar a -> Eff es a-readMVar = unsafeEff_ . M.readMVar+-- | Lifted 'M.readMVar''.+readMVar' :: Concurrent :> es => MVar' a -> Eff es a+readMVar' = unsafeEff_ . M.readMVar' --- | Lifted 'M.swapMVar' that evaluates the new value to WHNF.-swapMVar :: Concurrent :> es => MVar a -> a -> Eff es a-swapMVar var a = unsafeEff_ $ a `seq` M.swapMVar var a+-- | Lifted 'M.swapMVar''.+swapMVar' :: Concurrent :> es => MVar' a -> a -> Eff es a+swapMVar' var = unsafeEff_ . M.swapMVar' var --- | Lifted 'M.tryTakeMVar'.-tryTakeMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a)-tryTakeMVar = unsafeEff_ . M.tryTakeMVar+-- | Lifted 'M.tryTakeMVar''.+tryTakeMVar' :: Concurrent :> es => MVar' a -> Eff es (Maybe a)+tryTakeMVar' = unsafeEff_ . M.tryTakeMVar' --- | Lifted 'M.tryPutMVar' that evaluates the new value to WHNF.-tryPutMVar :: Concurrent :> es => MVar a -> a -> Eff es Bool-tryPutMVar var a = unsafeEff_ $ a `seq` M.tryPutMVar var a+-- | Lifted 'M.tryPutMVar''.+tryPutMVar' :: Concurrent :> es => MVar' a -> a -> Eff es Bool+tryPutMVar' var = unsafeEff_ . M.tryPutMVar' var --- | Lifted 'M.isEmptyMVar'.-isEmptyMVar :: Concurrent :> es => MVar a -> Eff es Bool-isEmptyMVar = unsafeEff_ . M.isEmptyMVar+-- | Lifted 'M.tryReadMVar''.+tryReadMVar' :: Concurrent :> es => MVar' a -> Eff es (Maybe a)+tryReadMVar' = unsafeEff_ . M.tryReadMVar' --- | Lifted 'M.tryReadMVar'.-tryReadMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a)-tryReadMVar = unsafeEff_ . M.tryReadMVar+-- | Lifted 'M.isEmptyMVar''.+isEmptyMVar' :: Concurrent :> es => MVar' a -> Eff es Bool+isEmptyMVar' = unsafeEff_ . M.isEmptyMVar' --- | Lifted 'M.withMVar'.-withMVar :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b-withMVar var f = reallyUnsafeUnliftIO $ \unlift -> do- M.withMVar var $ unlift . f+-- | Lifted 'M.withMVar''.+withMVar' :: Concurrent :> es => MVar' a -> (a -> Eff es b) -> Eff es b+withMVar' var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.withMVar' var $ unlift . f+{-# INLINE withMVar' #-} --- | Lifted 'M.withMVarMasked'.-withMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b-withMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do- M.withMVarMasked var $ unlift . f+-- | Lifted 'M.withMVar'Masked'.+withMVar'Masked :: Concurrent :> es => MVar' a -> (a -> Eff es b) -> Eff es b+withMVar'Masked var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.withMVar'Masked var $ unlift . f+{-# INLINE withMVar'Masked #-} --- | Lifted 'M.modifyMVar_' that evaluates the new value to WHNF.-modifyMVar_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()-modifyMVar_ var f = reallyUnsafeUnliftIO $ \unlift -> do- M.modifyMVar_ var $ \a0 -> do- a <- unlift $ f a0- a `seq` pure a+-- | Lifted 'M.modifyMVar'_'.+modifyMVar'_ :: Concurrent :> es => MVar' a -> (a -> Eff es a) -> Eff es ()+modifyMVar'_ var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.modifyMVar'_ var $ unlift . f+{-# INLINE modifyMVar'_ #-} --- | Lifted 'M.modifyMVar' that evaluates the new value to WHNF.-modifyMVar :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b-modifyMVar var f = reallyUnsafeUnliftIO $ \unlift -> do- M.modifyMVar var $ \a0 -> do- (a, b) <- unlift $ f a0- a `seq` pure (a, b)+-- | Lifted 'M.modifyMVar''.+modifyMVar' :: Concurrent :> es => MVar' a -> (a -> Eff es (a, b)) -> Eff es b+modifyMVar' var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.modifyMVar' var $ unlift . f+{-# INLINE modifyMVar' #-} --- | Lifted 'M.modifyMVarMasked_' that evaluates the new value to WHNF.-modifyMVarMasked_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()-modifyMVarMasked_ var f = reallyUnsafeUnliftIO $ \unlift -> do- M.modifyMVarMasked_ var $ \a0 -> do- a <- unlift $ f a0- a `seq` pure a+-- | Lifted 'M.modifyMVar'Masked_'.+modifyMVar'Masked_ :: Concurrent :> es => MVar' a -> (a -> Eff es a) -> Eff es ()+modifyMVar'Masked_ var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.modifyMVar'Masked_ var $ unlift . f+{-# INLINE modifyMVar'Masked_ #-} --- | Lifted 'M.modifyMVarMasked' that evaluates the new value to WHNF.-modifyMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b-modifyMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do- M.modifyMVarMasked var $ \a0 -> do- a <- unlift $ f a0- a `seq` pure a+-- | Lifted 'M.modifyMVar'Masked'.+modifyMVar'Masked :: Concurrent :> es => MVar' a -> (a -> Eff es (a, b)) -> Eff es b+modifyMVar'Masked var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.modifyMVar'Masked var $ unlift . f+{-# INLINE modifyMVar'Masked #-} --- | Lifted 'M.mkWeakMVar'.-mkWeakMVar :: Concurrent :> es => MVar a -> Eff es () -> Eff es (Weak (MVar a))-mkWeakMVar var f = unsafeEff $ \es -> do+-- | Lifted 'M.mkWeakMVar''.+--+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes+-- to thread local data will not be visible outside of it.+mkWeakMVar'+ :: (HasCallStack, Concurrent :> es)+ => MVar' a -> Eff es ()+ -> Eff es (Weak (MVar' a))+mkWeakMVar' var f = unsafeEff $ \es -> do -- The finalizer can run at any point and in any thread.- M.mkWeakMVar var . unEff f =<< cloneEnv es+ M.mkWeakMVar' var . unEff f =<< cloneEnv es
+ src/Effectful/Concurrent/MVar/Strict/Compat.hs view
@@ -0,0 +1,138 @@+-- | Lifted "Control.Concurrent.MVar" with operations that force values put+-- inside an 'MVar' to WHNF.+module Effectful.Concurrent.MVar.Strict.Compat+ {-# DEPRECATED "Use Effectful.Concurrent.MVar.Strict" #-}+ ( -- * Effect+ Concurrent++ -- ** Handlers+ , runConcurrent++ -- * MVar+ , MVar+ , newEmptyMVar+ , newMVar+ , takeMVar+ , putMVar+ , readMVar+ , swapMVar+ , tryTakeMVar+ , tryPutMVar+ , isEmptyMVar+ , withMVar+ , withMVarMasked+ , modifyMVar+ , modifyMVar_+ , modifyMVarMasked+ , modifyMVarMasked_+ , tryReadMVar+ , mkWeakMVar+ ) where++import System.Mem.Weak (Weak)+import Control.Exception (evaluate)+import Control.Concurrent.MVar (MVar)+import Control.Concurrent.MVar qualified as M++import Effectful+import Effectful.Concurrent.Effect+import Effectful.Dispatch.Static+import Effectful.Dispatch.Static.Primitive+import Effectful.Dispatch.Static.Unsafe++-- | Lifted 'M.newEmptyMVar'.+newEmptyMVar :: Concurrent :> es => Eff es (MVar a)+newEmptyMVar = unsafeEff_ M.newEmptyMVar++-- | Lifted 'M.newMVar' that evaluates the value to WHNF.+newMVar :: Concurrent :> es => a -> Eff es (MVar a)+newMVar a = unsafeEff_ $ M.newMVar =<< evaluate a++-- | Lifted 'M.takeMVar'.+takeMVar :: Concurrent :> es => MVar a -> Eff es a+takeMVar = unsafeEff_ . M.takeMVar++-- | Lifted 'M.putMVar'.+putMVar :: Concurrent :> es => MVar a -> a -> Eff es ()+putMVar var a = unsafeEff_ $ M.putMVar var =<< evaluate a++-- | Lifted 'M.readMVar'.+readMVar :: Concurrent :> es => MVar a -> Eff es a+readMVar = unsafeEff_ . M.readMVar++-- | Lifted 'M.swapMVar' that evaluates the new value to WHNF.+swapMVar :: Concurrent :> es => MVar a -> a -> Eff es a+swapMVar var a = unsafeEff_ $ M.swapMVar var =<< evaluate a++-- | Lifted 'M.tryTakeMVar'.+tryTakeMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a)+tryTakeMVar = unsafeEff_ . M.tryTakeMVar++-- | Lifted 'M.tryPutMVar' that evaluates the new value to WHNF.+tryPutMVar :: Concurrent :> es => MVar a -> a -> Eff es Bool+tryPutMVar var a = unsafeEff_ $ M.tryPutMVar var =<< evaluate a++-- | Lifted 'M.isEmptyMVar'.+isEmptyMVar :: Concurrent :> es => MVar a -> Eff es Bool+isEmptyMVar = unsafeEff_ . M.isEmptyMVar++-- | Lifted 'M.tryReadMVar'.+tryReadMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a)+tryReadMVar = unsafeEff_ . M.tryReadMVar++-- | Lifted 'M.withMVar'.+withMVar :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b+withMVar var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.withMVar var $ unlift . f+{-# INLINE withMVar #-}++-- | Lifted 'M.withMVarMasked'.+withMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b+withMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.withMVarMasked var $ unlift . f+{-# INLINE withMVarMasked #-}++-- | Lifted 'M.modifyMVar_' that evaluates the new value to WHNF.+modifyMVar_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()+modifyMVar_ var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.modifyMVar_ var $ \a0 -> do+ a <- unlift $ f a0+ evaluate a+{-# INLINE modifyMVar_ #-}++-- | Lifted 'M.modifyMVar' that evaluates the new value to WHNF.+modifyMVar :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b+modifyMVar var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.modifyMVar var $ \a0 -> do+ (a, b) <- unlift $ f a0+ (, b) <$> evaluate a+{-# INLINE modifyMVar #-}++-- | Lifted 'M.modifyMVarMasked_' that evaluates the new value to WHNF.+modifyMVarMasked_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()+modifyMVarMasked_ var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.modifyMVarMasked_ var $ \a0 -> do+ a <- unlift $ f a0+ evaluate a+{-# INLINE modifyMVarMasked_ #-}++-- | Lifted 'M.modifyMVarMasked' that evaluates the new value to WHNF.+modifyMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b+modifyMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do+ M.modifyMVarMasked var $ \a0 -> do+ (a, b) <- unlift $ f a0+ (, b) <$> evaluate a+{-# INLINE modifyMVarMasked #-}++-- | Lifted 'M.mkWeakMVar'.+--+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes+-- to thread local data will not be visible outside of it.+mkWeakMVar+ :: (HasCallStack, Concurrent :> es)+ => MVar a+ -> Eff es ()+ -> Eff es (Weak (MVar a))+mkWeakMVar var f = unsafeEff $ \es -> do+ -- The finalizer can run at any point and in any thread.+ M.mkWeakMVar var . unEff f =<< cloneEnv es
src/Effectful/Concurrent/QSem.hs view
@@ -14,7 +14,7 @@ ) where import Control.Concurrent.QSem (QSem)-import qualified Control.Concurrent.QSem as Q+import Control.Concurrent.QSem qualified as Q import Effectful import Effectful.Concurrent.Effect
src/Effectful/Concurrent/QSemN.hs view
@@ -14,7 +14,7 @@ ) where import Control.Concurrent.QSemN (QSemN)-import qualified Control.Concurrent.QSemN as Q+import Control.Concurrent.QSemN qualified as Q import Effectful import Effectful.Concurrent.Effect
src/Effectful/Concurrent/STM.hs view
@@ -37,6 +37,7 @@ , STM.takeTMVar , STM.putTMVar , STM.readTMVar+ , STM.writeTMVar , STM.tryReadTMVar , STM.swapTMVar , STM.tryTakeTMVar@@ -90,7 +91,7 @@ ) where import Control.Concurrent.STM (STM, TVar, TMVar, TChan, TQueue, TBQueue)-import qualified Control.Concurrent.STM as STM+import Control.Concurrent.STM qualified as STM import System.Mem.Weak (Weak) import GHC.Natural (Natural) @@ -116,7 +117,14 @@ registerDelay = unsafeEff_ . STM.registerDelay -- | Lifted 'STM.mkWeakTVar'.-mkWeakTVar :: Concurrent :> es => TVar a -> Eff es () -> Eff es (Weak (TVar a))+--+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes+-- to thread local data will not be visible outside of it.+mkWeakTVar+ :: (HasCallStack, Concurrent :> es)+ => TVar a+ -> Eff es ()+ -> Eff es (Weak (TVar a)) mkWeakTVar var f = unsafeEff $ \es -> do -- The finalizer can run at any point and in any thread. STM.mkWeakTVar var . unEff f =<< cloneEnv es@@ -130,7 +138,14 @@ newEmptyTMVarIO = unsafeEff_ STM.newEmptyTMVarIO -- | Lifted 'STM.mkWeakTMVar'.-mkWeakTMVar :: Concurrent :> es => TMVar a -> Eff es () -> Eff es (Weak (TMVar a))+--+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes+-- to thread local data will not be visible outside of it.+mkWeakTMVar+ :: (HasCallStack, Concurrent :> es)+ => TMVar a+ -> Eff es ()+ -> Eff es (Weak (TMVar a)) mkWeakTMVar var f = unsafeEff $ \es -> do -- The finalizer can run at any point and in any thread. STM.mkWeakTMVar var . unEff f =<< cloneEnv es
src/Effectful/Console/ByteString.hs view
@@ -4,9 +4,9 @@ -- to avoid name clashes with the functions provided by "Prelude", e.g.: -- -- > import Data.ByteString (ByteString)--- > import qualified Data.ByteString.Char8 as BS8+-- > import Data.ByteString.Char8 qualified as BS8 -- > import Effectful.Console.ByteString (Console)--- > import qualified Effectful.Console.ByteString as Console+-- > import Effectful.Console.ByteString qualified as Console -- module Effectful.Console.ByteString ( -- * Effect@@ -32,7 +32,7 @@ ) import Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as BS8+import Data.ByteString.Char8 qualified as BS8 import Effectful import Effectful.Console.Effect
src/Effectful/Console/ByteString/Lazy.hs view
@@ -4,9 +4,9 @@ -- to avoid name clashes with the functions provided by "Prelude", e.g.: -- -- > import Data.ByteString.Lazy.Char8 (ByteString)--- > import qualified Data.ByteString.Lazy.Char8 as LBS8+-- > import Data.ByteString.Lazy.Char8 qualified as LBS8 -- > import Effectful.Console.ByteString.Lazy (Console)--- > import qualified Effectful.Console.ByteString.Lazy as Console+-- > import Effectful.Console.ByteString.Lazy qualified as Console -- module Effectful.Console.ByteString.Lazy ( -- * Effect@@ -30,7 +30,7 @@ ) import Data.ByteString.Lazy (ByteString)-import qualified Data.ByteString.Lazy.Char8 as LBS8+import Data.ByteString.Lazy.Char8 qualified as LBS8 import Effectful import Effectful.Console.Effect
src/Effectful/Console/Effect.hs view
@@ -17,5 +17,5 @@ data instance StaticRep Console = Console -- | Run the 'Console' effect.-runConsole :: IOE :> es => Eff (Console : es) a -> Eff es a+runConsole :: (HasCallStack, IOE :> es) => Eff (Console : es) a -> Eff es a runConsole = evalStaticRep Console
src/Effectful/Environment.hs view
@@ -20,7 +20,7 @@ , withProgName ) where -import qualified System.Environment as E+import System.Environment qualified as E import Effectful import Effectful.Dispatch.Static@@ -32,7 +32,7 @@ data instance StaticRep Environment = Environment -- | Run the 'Environment' effect.-runEnvironment :: IOE :> es => Eff (Environment : es) a -> Eff es a+runEnvironment :: (HasCallStack, IOE :> es) => Eff (Environment : es) a -> Eff es a runEnvironment = evalStaticRep Environment -- | Lifted 'E.getArgs'.
src/Effectful/FileSystem.hs view
@@ -92,7 +92,7 @@ ) where import Data.Time (UTCTime)-import qualified System.Directory as D+import System.Directory qualified as D import Effectful import Effectful.Dispatch.Static
src/Effectful/FileSystem/Effect.hs view
@@ -16,5 +16,5 @@ data instance StaticRep FileSystem = FileSystem -- | Run the 'FileSystem' effect.-runFileSystem :: IOE :> es => Eff (FileSystem : es) a -> Eff es a+runFileSystem :: (HasCallStack, IOE :> es) => Eff (FileSystem : es) a -> Eff es a runFileSystem = evalStaticRep FileSystem
src/Effectful/FileSystem/IO.hs view
@@ -39,7 +39,7 @@ ) where import System.IO (Handle, IOMode (..))-import qualified System.IO as IO+import System.IO qualified as IO import Effectful import Effectful.Dispatch.Static
src/Effectful/FileSystem/IO/ByteString.hs view
@@ -5,8 +5,8 @@ -- to avoid name clashes with the functions provided by "Prelude", e.g.: -- -- > import Data.ByteString (ByteString)--- > import qualified Data.ByteString as BS--- > import qualified Effectful.FileSystem.IO.ByteString as EBS+-- > import Data.ByteString qualified as BS+-- > import Effectful.FileSystem.IO.ByteString qualified as EBS -- module Effectful.FileSystem.IO.ByteString #if MIN_VERSION_bytestring(0,11,2)@@ -35,20 +35,18 @@ , hPutStrLn ) where -import Prelude hiding- ( appendFile- , readFile- , writeFile- )- import Data.ByteString (ByteString)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as BS8+import Data.ByteString.Char8 qualified as BS8+import Prelude hiding (appendFile, readFile, writeFile) import System.IO (Handle) import Effectful import Effectful.Dispatch.Static import Effectful.FileSystem++#if MIN_VERSION_bytestring(0,11,2)+import Data.ByteString qualified as BS+#endif ---------------------------------------- -- Introducing and eliminating ByteStrings
src/Effectful/FileSystem/IO/ByteString/Builder.hs view
@@ -5,8 +5,8 @@ -- to avoid name clashes with the functions provided by "Prelude", e.g.: -- -- > import Data.ByteString.Builder (Builder)--- > import qualified Data.ByteString.Builder as BSB--- > import qualified Effectful.FileSystem.IO.ByteString.Builder as EBSB+-- > import Data.ByteString.Builder qualified as BSB+-- > import Effectful.FileSystem.IO.ByteString.Builder qualified as EBSB -- module Effectful.FileSystem.IO.ByteString.Builder ( -- * Executing Builders@@ -16,10 +16,9 @@ #endif ) where -import Prelude hiding (writeFile)- import Data.ByteString.Builder (Builder)-import qualified Data.ByteString.Builder as BSB+import Data.ByteString.Builder qualified as BSB+import Prelude hiding (writeFile) import System.IO (Handle) import Effectful
src/Effectful/FileSystem/IO/ByteString/Lazy.hs view
@@ -3,8 +3,8 @@ -- to avoid name clashes with the functions provided by "Prelude", e.g.: -- -- > import Data.ByteString.Lazy (ByteString)--- > import qualified Data.ByteString.Lazy.Char8 as LBS8--- > import qualified Effectful.FileSystem.IO.ByteString.Lazy as ELBS+-- > import Data.ByteString.Lazy.Char8 qualified as LBS8+-- > import Effectful.FileSystem.IO.ByteString.Lazy qualified as ELBS -- module Effectful.FileSystem.IO.ByteString.Lazy ( -- * Files@@ -29,7 +29,7 @@ ) import Data.ByteString.Lazy (ByteString)-import qualified Data.ByteString.Lazy.Char8 as LBS8+import Data.ByteString.Lazy.Char8 qualified as LBS8 import System.IO (Handle) import Effectful
src/Effectful/FileSystem/IO/File.hs view
@@ -15,7 +15,7 @@ import Data.ByteString (ByteString) import System.IO (Handle, IOMode (..))-import qualified UnliftIO.IO.File as U+import UnliftIO.IO.File qualified as U import Effectful import Effectful.Dispatch.Static
+ src/Effectful/Prim/IORef.hs view
@@ -0,0 +1,79 @@+-- | Lifted "Data.IORef".+--+-- /Note:/ it requires 'Prim' because @MutVar@ from the @primitive@ library is a+-- generalization of 'IORef'.+--+-- @since 2.4.0.0+module Effectful.Prim.IORef+ ( -- * Effect+ Prim++ -- ** Handlers+ , runPrim++ -- * IORef+ , IORef+ , newIORef+ , readIORef+ , writeIORef+ , modifyIORef+ , modifyIORef'+ , atomicModifyIORef+ , atomicModifyIORef'+ , atomicWriteIORef+ , mkWeakIORef+ ) where++import Data.IORef (IORef)+import Data.IORef qualified as Ref+import System.Mem.Weak (Weak)++import Effectful+import Effectful.Dispatch.Static+import Effectful.Dispatch.Static.Primitive+import Effectful.Prim++-- | Lifted 'Ref.newIORef'.+newIORef :: Prim :> es => a -> Eff es (IORef a)+newIORef = unsafeEff_ . Ref.newIORef++-- | Lifted 'Ref.readIORef'.+readIORef :: Prim :> es => IORef a -> Eff es a+readIORef = unsafeEff_ . Ref.readIORef++-- | Lifted 'Ref.writeIORef'.+writeIORef :: Prim :> es => IORef a -> a -> Eff es ()+writeIORef var = unsafeEff_ . Ref.writeIORef var++-- | Lifted 'Ref.modifyIORef'.+modifyIORef :: Prim :> es => IORef a -> (a -> a) -> Eff es ()+modifyIORef var = unsafeEff_ . Ref.modifyIORef var++-- | Lifted 'Ref.modifyIORef''.+modifyIORef' :: Prim :> es => IORef a -> (a -> a) -> Eff es ()+modifyIORef' var = unsafeEff_ . Ref.modifyIORef' var++-- | Lifted 'Ref.atomicModifyIORef'.+atomicModifyIORef :: Prim :> es => IORef a -> (a -> (a, b)) -> Eff es b+atomicModifyIORef var = unsafeEff_ . Ref.atomicModifyIORef var++-- | Lifted 'Ref.atomicModifyIORef''.+atomicModifyIORef' :: Prim :> es => IORef a -> (a -> (a, b)) -> Eff es b+atomicModifyIORef' var = unsafeEff_ . Ref.atomicModifyIORef' var++-- | Lifted 'Ref.atomicWriteIORef''.+atomicWriteIORef :: Prim :> es => IORef a -> a -> Eff es ()+atomicWriteIORef var = unsafeEff_ . Ref.atomicWriteIORef var++-- | Lifted 'Ref.mkWeakIORef'.+--+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes+-- to thread local data will not be visible outside of it.+mkWeakIORef+ :: (HasCallStack, Prim :> es)+ => IORef a+ -> Eff es ()+ -> Eff es (Weak (IORef a))+mkWeakIORef var f = unsafeEff $ \es -> do+ -- The finalizer can run at any point and in any thread.+ Ref.mkWeakIORef var . unEff f =<< cloneEnv es
+ src/Effectful/Prim/IORef/Strict.hs view
@@ -0,0 +1,66 @@+-- | Lifted "Data.IORef.Strict".+--+-- @since 2.4.0.0+module Effectful.Prim.IORef.Strict+ ( -- * Effect+ Prim++ -- ** Handlers+ , runPrim++ -- * IORef+ , IORef'+ , newIORef'+ , readIORef'+ , writeIORef'+ , modifyIORef'+ , atomicModifyIORef'+ , atomicWriteIORef'+ , mkWeakIORef'+ ) where++import Data.IORef.Strict (IORef')+import Data.IORef.Strict qualified as Ref+import System.Mem.Weak (Weak)++import Effectful+import Effectful.Dispatch.Static+import Effectful.Dispatch.Static.Primitive+import Effectful.Prim++-- | Lifted 'Ref.newIORef''.+newIORef' :: Prim :> es => a -> Eff es (IORef' a)+newIORef' = unsafeEff_ . Ref.newIORef'++-- | Lifted 'Ref.readIORef''.+readIORef' :: Prim :> es => IORef' a -> Eff es a+readIORef' = unsafeEff_ . Ref.readIORef'++-- | Lifted 'Ref.writeIORef''.+writeIORef' :: Prim :> es => IORef' a -> a -> Eff es ()+writeIORef' var = unsafeEff_ . Ref.writeIORef' var++-- | Lifted 'Ref.modifyIORef''.+modifyIORef' :: Prim :> es => IORef' a -> (a -> a) -> Eff es ()+modifyIORef' var = unsafeEff_ . Ref.modifyIORef' var++-- | Lifted 'Ref.atomicModifyIORef''.+atomicModifyIORef' :: Prim :> es => IORef' a -> (a -> (a, b)) -> Eff es b+atomicModifyIORef' var = unsafeEff_ . Ref.atomicModifyIORef' var++-- | Lifted 'Ref.atomicWriteIORef''.+atomicWriteIORef' :: Prim :> es => IORef' a -> a -> Eff es ()+atomicWriteIORef' var = unsafeEff_ . Ref.atomicWriteIORef' var++-- | Lifted 'Ref.mkWeakIORef''.+--+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes+-- to thread local data will not be visible outside of it.+mkWeakIORef'+ :: (HasCallStack, Prim :> es)+ => IORef' a+ -> Eff es ()+ -> Eff es (Weak (IORef' a))+mkWeakIORef' var f = unsafeEff $ \es -> do+ -- The finalizer can run at any point and in any thread.+ Ref.mkWeakIORef' var . unEff f =<< cloneEnv es
src/Effectful/Process.hs view
@@ -51,7 +51,7 @@ import System.Exit (ExitCode) import System.IO (Handle) import System.Posix.Internals (FD)-import qualified System.Process as P+import System.Process qualified as P import Effectful import Effectful.Dispatch.Static@@ -62,7 +62,7 @@ type instance DispatchOf Process = Static WithSideEffects data instance StaticRep Process = Process -runProcess :: IOE :> es => Eff (Process : es) a -> Eff es a+runProcess :: (HasCallStack, IOE :> es) => Eff (Process : es) a -> Eff es a runProcess = evalStaticRep Process ----------------------------------------
src/Effectful/Temporary.hs view
@@ -13,7 +13,7 @@ ) where import System.IO-import qualified UnliftIO.Temporary as T+import UnliftIO.Temporary qualified as T import Effectful import Effectful.Dispatch.Static@@ -25,7 +25,7 @@ data instance StaticRep Temporary = Temporary -- | Run the 'Temporary' effect.-runTemporary :: IOE :> es => Eff (Temporary : es) a -> Eff es a+runTemporary :: (HasCallStack, IOE :> es) => Eff (Temporary : es) a -> Eff es a runTemporary = evalStaticRep Temporary -- | Lifted 'T.withSystemTempFile'.
src/Effectful/Timeout.hs view
@@ -9,7 +9,7 @@ , timeout ) where -import qualified System.Timeout as T+import System.Timeout qualified as T import Effectful import Effectful.Dispatch.Static@@ -21,7 +21,7 @@ data instance StaticRep Timeout = Timeout -- | Run the 'Timeout' effect.-runTimeout :: IOE :> es => Eff (Timeout : es) a -> Eff es a+runTimeout :: (HasCallStack, IOE :> es) => Eff (Timeout : es) a -> Eff es a runTimeout = evalStaticRep Timeout -- | Lifted 'T.timeout'.
tests/AsyncTests.hs view
@@ -2,16 +2,16 @@ import Control.Concurrent (threadDelay) import Control.Monad+import Data.Set qualified as S import Test.Tasty import Test.Tasty.HUnit-import qualified Data.Set as S import Effectful import Effectful.Concurrent.Async import Effectful.Error.Static import Effectful.State.Dynamic -import qualified Utils as U+import Utils qualified as U asyncTests :: TestTree asyncTests = testGroup "Async"
tests/ConcurrencyTests.hs view
@@ -2,15 +2,17 @@ import Control.Concurrent (threadDelay) import Control.Monad+import Data.Set qualified as S import Test.Tasty import Test.Tasty.HUnit import UnliftIO-import qualified Data.Set as S import Effectful+import Effectful.Concurrent.Async qualified as E+import Effectful.Dispatch.Dynamic import Effectful.Error.Static import Effectful.State.Dynamic-import qualified Utils as U+import Utils qualified as U concurrencyTests :: TestTree concurrencyTests = testGroup "Concurrency"@@ -20,6 +22,8 @@ , testCase "unlifting several times" test_unliftMany , testCase "async with unmask" test_asyncWithUnmask , testCase "pooled workers" test_pooledWorkers+ , testCase "using local unlift correctly works" test_correctLocalUnlift+ , testCase "using local unlift incorrectly doesn't work" test_wrongLocalUnlift ] test_localState :: Assertion@@ -119,3 +123,34 @@ where n = 10 threads = 4++test_correctLocalUnlift :: Assertion+test_correctLocalUnlift = runEff . E.runConcurrent $ do+ x <- runFork . send . RunAsyncCorrect $ pure ()+ E.wait x++test_wrongLocalUnlift :: Assertion+test_wrongLocalUnlift = runEff . E.runConcurrent $ do+ U.assertThrowsErrorCall "invalid LocalEnv use" $ do+ x <- runFork . send . RunAsyncWrong $ pure ()+ E.wait x++data Fork :: Effect where+ RunAsyncCorrect :: m a -> Fork m (E.Async a)+ RunAsyncWrong :: m a -> Fork m (E.Async a)+type instance DispatchOf Fork = Dynamic++runFork :: (IOE :> es, E.Concurrent :> es) => Eff (Fork : es) a -> Eff es a+runFork = interpret $ \env -> \case+ RunAsyncCorrect action -> do+ -- LocalEnv is correctly used in the thread in belongs to, so creation of+ -- the unlifting function should succeed.+ localUnlift env strategy $ \unlift -> do+ E.async $ unlift action+ RunAsyncWrong action -> E.async $ do+ -- LocalEnv is incorrectly passed to a different thread, so creation of the+ -- unlifting function should fail.+ localUnlift env strategy $ \unlift -> do+ unlift action+ where+ strategy = ConcUnlift Ephemeral $ Limited 1
tests/EnvTests.hs view
@@ -12,7 +12,7 @@ import Effectful.Reader.Static import Effectful.Provider import Effectful.State.Static.Local-import qualified Utils as U+import Utils qualified as U envTests :: TestTree envTests = testGroup "Env"@@ -22,6 +22,7 @@ , testCase "unsafeCoerce doesn't work" test_noUnsafeCoerce , testCase "interpose works" test_interpose , testCase "interpose/provider works" test_interposeProvider+ , testCase "borrow/lend works" test_borrowLend ] test_tailEnv :: Assertion@@ -103,8 +104,6 @@ test_noUnsafeCoerce = do r1 <- try @ErrorCall . evaluate $ unsafeCoerce1 @Int 'a' assertBool "unsafeCoerce1" (isLeft r1)- r2 <- try @ErrorCall . evaluate $ unsafeCoerce2 @Int 'a'- assertBool "unsafeCoerce2" (isLeft r2) unsafeCoerce1 :: forall b a. a -> b unsafeCoerce1 a = runPureEff $ do@@ -121,26 +120,6 @@ Box b <- runReader (Box a) $ raise oops pure b -unsafeCoerce2 :: forall b a. a -> b-unsafeCoerce2 a = runPureEff $ do- backupEs <- unsafeEff cloneEnv- -- '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)- -- If restoreEnv messes up versioning (i.e. restores the version counter from- -- before the above Reader was used), below code will succeed because the- -- Reader (Box a) will have the same version as Reader (Box b).- unsafeEff $ \es -> restoreEnv es backupEs- -- Put Reader (Box a) where the Reader (Box b) was before and attempt to- -- retrieve 'a' coerced to 'b'. It should fail 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 ----------------------------------------@@ -191,11 +170,11 @@ type instance DispatchOf A = Dynamic runA :: Int -> Eff (A : es) a -> Eff es a-runA n = interpret $ \_ -> \case+runA n = interpret_ $ \case A -> pure n doubleA :: A :> es => Eff es a -> Eff es a-doubleA = interpose $ \_ -> \case+doubleA = interpose_ $ \case A -> (+) <$> send A <*> send A data B :: Effect where@@ -203,9 +182,41 @@ type instance DispatchOf B = Dynamic runB :: A :> es => Eff (B : es) a -> Eff es a-runB = interpret $ \_ -> \case+runB = interpret_ $ \case B -> send A doubleB :: B :> es => Eff es a -> Eff es a-doubleB = interpose $ \_ -> \case+doubleB = interpose_ $ \case B -> (+) <$> send B <*> send B++----------------------------------------++test_borrowLend :: Assertion+test_borrowLend = runEff $ do+ runX 1 2 . evalState @[Int] [3] . runReader () . runReader @[Int] [4] $ do+ U.assertEqual "expected result" [1,2,3,4,1,2,3,4] =<< send X++data X :: Effect where+ X :: (State [Int] :> es, Reader [Int] :> es, Reader () :> es) => X (Eff es) [Int]+type instance DispatchOf X = Dynamic++runX :: Int -> Int -> Eff (X : es) a -> Eff es a+runX s0 r0 = reinterpret (evalState s0 . evalState () . runReader r0) $ \env -> \case+ X -> localSeqUnlift env $ \unlift -> do+ as <- localSeqLend @[State Int, Reader Int] env $ \withHandlerEffs -> do+ unlift . withHandlerEffs $ do+ () <- ask+ s <- get @Int+ r <- ask @Int+ ss <- get @[Int]+ rs <- ask @[Int]+ pure $ [s, r] ++ ss ++ rs+ bs <- localSeqBorrow @[Reader [Int], State [Int], Reader ()] env $ \withEffs -> do+ withEffs $ do+ () <- ask+ s <- get @Int+ r <- ask @Int+ ss <- get @[Int]+ rs <- ask @[Int]+ pure $ [s, r] ++ ss ++ rs+ pure $ as ++ bs
tests/EnvironmentTests.hs view
@@ -6,7 +6,7 @@ import Effectful import Effectful.Environment-import qualified Utils as U+import Utils qualified as U environmentTests :: TestTree environmentTests = testCaseSteps "Environment" $ \step -> do
tests/ErrorTests.hs view
@@ -5,11 +5,13 @@ import Effectful import Effectful.Dispatch.Dynamic+import Effectful.Error.Dynamic qualified as D import Effectful.Error.Static errorTests :: TestTree errorTests = testGroup "Error" [ testCase "different handlers are independent" test_independentHandlers+ , testCase "call stack of dynamic throwError doesn't show internal details" test_dynamicThrowErrorCallStack ] test_independentHandlers :: Assertion@@ -21,6 +23,13 @@ "outerThrow" == fst (last $ getCallStack cs) Right _ -> assertFailure "error caught by the wrong (inner) handler" +test_dynamicThrowErrorCallStack :: Assertion+test_dynamicThrowErrorCallStack = do+ Left (cs, ()) <- runEff . D.runError @() $ D.throwError ()+ case getCallStack cs of+ [("throwError", _)] -> pure ()+ _ -> assertFailure $ "invalid call stack: " ++ prettyCallStack cs+ ---------------------------------------- -- Helpers @@ -33,5 +42,5 @@ outerThrow = send OuterThrow runOuterThrow :: Error String :> es => Eff (OuterThrow : es) a -> Eff es a-runOuterThrow = interpret $ \_ -> \case+runOuterThrow = interpret_ $ \case OuterThrow -> throwError "outer"
+ tests/LabeledTests.hs view
@@ -0,0 +1,61 @@+module LabeledTests (labeledTests) where++import Test.Tasty+import Test.Tasty.HUnit++import Effectful+import Effectful.Dispatch.Dynamic+import Effectful.Labeled+import Effectful.Reader.Static+import Utils qualified as U++labeledTests :: TestTree+labeledTests = testGroup "Labeled"+ [ testCase "labeled behaves correctly" $ test_labeledBehavior+ , testCase "(labeled . send) and (send . Labeled) behave the same" $ test_labeledSend+ ]++test_labeledBehavior :: Assertion+test_labeledBehavior = do+ v <- runEff+ . runLabeled @"a" (runReader "a")+ . runLabeled @"b" (runReader "b")+ . runReader "c"+ $ action+ assertEqual "expected result" "abc" v+ where+ action+ :: ( Labeled "a" (Reader String) :> es+ , Labeled "b" (Reader String) :> es+ , Reader String :> es+ )+ => Eff es String+ action = do+ a <- labeled @"b" @(Reader String) $ do+ labeled @"a" @(Reader String) $ do+ ask+ b <- labeled @"b" @(Reader String) $ do+ ask+ c <- ask+ pure $ a ++ b ++ c++test_labeledSend :: Assertion+test_labeledSend = runEff $ do+ runX 1 . runLabeled @"x" (runX 2) $ do+ v0 <- send X2+ U.assertEqual "expected result" 1 v0+ v1 <- (labeled @"x" @X . send) X2+ U.assertEqual "expected result" 2 v1+ v2 <- (send . Labeled @"x") X2+ U.assertEqual "expected result" 2 v2++data X :: Effect where+ X1 :: X m Int+ X2 :: (X :> es, Labeled "x" X :> es) => X (Eff es) Int++type instance DispatchOf X = Dynamic++runX :: Int -> Eff (X : es) a -> Eff es a+runX x = interpret $ \env -> \case+ X1 -> pure x+ X2 -> localSeqUnlift env $ \unlift -> unlift $ send X1
tests/Main.hs view
@@ -7,6 +7,7 @@ import EnvTests import EnvironmentTests import ErrorTests+import LabeledTests import NonDetTests import PrimTests import ReaderTests@@ -21,6 +22,7 @@ , envTests , environmentTests , errorTests+ , labeledTests , nonDetTests , primTests , readerTests
tests/NonDetTests.hs view
@@ -9,7 +9,7 @@ import Effectful.Reader.Static import Effectful.State.Dynamic -import qualified Utils as U+import Utils qualified as U nonDetTests :: TestTree nonDetTests = testGroup "NonDet"@@ -30,11 +30,11 @@ expectedLocalState :: OnEmptyPolicy -> Int expectedLocalState = \case- OnEmptyKeep -> 3- OnEmptyRollback -> 2+ OnEmptyKeep -> 7+ OnEmptyRollback -> 0 expectedSharedState :: OnEmptyPolicy -> Int- expectedSharedState _ = 3+ expectedSharedState _ = 7 test_empty :: Eff [NonDet, IOE] Bool@@ -53,12 +53,15 @@ test_state evalState expectedState step = runEff $ do evalState 0 . runNonDetBoth test $ \policy result -> do liftIO . step $ show policy- U.assertEqual "result" (Just ()) (dropLeft result)+ U.assertEqual "result" Nothing (dropLeft result) s <- state @Int $ \s -> (s, 0) U.assertEqual "state" (expectedState policy) s where test :: (NonDet :> es, State Int :> es) => Eff es ()- test = (modify @Int (+1) >> empty) <|> modify @Int (+2)+ test = do+ modify @Int (+1)+ _<- (modify @Int (+2) >> empty) <|> (modify @Int (+4) >> empty)+ modify @Int (+8) test_independentHandlers :: (String -> IO ()) -> Assertion test_independentHandlers step = runEff $ do@@ -84,7 +87,7 @@ outerEmpty = send OuterEmpty runOuterEmpty :: NonDet :> es => Eff (OuterEmpty : es) a -> Eff es a-runOuterEmpty = interpret $ \_ -> \case+runOuterEmpty = interpret_ $ \case OuterEmpty -> emptyEff ----
tests/PrimTests.hs view
@@ -3,7 +3,7 @@ import Data.Primitive.MutVar import Test.Tasty import Test.Tasty.HUnit-import qualified Utils as U+import Utils qualified as U import Effectful import Effectful.Prim
tests/ReaderTests.hs view
@@ -5,13 +5,14 @@ import Effectful import Effectful.Dispatch.Dynamic-import Effectful.Reader.Dynamic-import qualified Utils as U+import Effectful.Reader.Dynamic qualified as D+import Effectful.Reader.Static qualified as S+import Utils qualified as U readerTests :: TestTree readerTests = testGroup "Reader"- [ testCase "local works in handlers (dynamic/static)" $ test_localInHandler runReader- , testCase "local works in handlers (dynamic/pure)" $ test_localInHandler runPureReader+ [ testCase "local works in handlers (static)" test_localInHandlerStatic+ , testCase "local works in handlers (dynamic)" test_localInHandlerDynamic ] data SomeEff :: Effect where@@ -19,21 +20,16 @@ type instance DispatchOf SomeEff = Dynamic -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+test_localInHandlerStatic :: Assertion+test_localInHandlerStatic = runEff . S.runReader "global" . interpret f $ do+ S.local (const "local") $ send SomeAction where- f :: (IOE :> es, Reader String :> es) => EffectHandler SomeEff es- f _ SomeAction = U.assertEqual "expected result" "local" =<< ask+ f :: (IOE :> es, S.Reader String :> es) => EffectHandler SomeEff es+ f _ SomeAction = U.assertEqual "expected result" "local" =<< S.ask --- | Purely dynamic Reader for testing purposes.-runPureReader :: r -> Eff (Reader r : es) a -> Eff es a-runPureReader r0 = interpret (handler r0)+test_localInHandlerDynamic :: Assertion+test_localInHandlerDynamic = runEff . D.runReader "global" . interpret f $ do+ D.local (const "local") $ send SomeAction 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+ f :: (IOE :> es, D.Reader String :> es) => EffectHandler SomeEff es+ f _ SomeAction = U.assertEqual "expected result" "local" =<< D.ask
tests/StateTests.hs view
@@ -1,15 +1,20 @@ module StateTests (stateTests) where +import Control.Exception.Lifted qualified as LE+import Control.Exception.Safe qualified as Safe+import Control.Monad+import Control.Monad.Catch qualified as C+import Data.IORef.Strict import Test.Tasty import Test.Tasty.HUnit-import qualified Control.Monad.Catch as E-import qualified Control.Exception.Lifted as LE-import qualified UnliftIO.Exception as UE+import UnliftIO.Exception qualified as UE import Effectful import Effectful.Dispatch.Dynamic+import Effectful.Dispatch.Static+import Effectful.Internal.Env import Effectful.State.Static.Local-import qualified Utils as U+import Utils qualified as U stateTests :: TestTree stateTests = testGroup "State"@@ -34,9 +39,19 @@ test_stateM :: Assertion test_stateM = runEff $ do- (a, b) <- runState "hi" . stateM $ \s -> pure (s, s ++ "!!!")+ (a, b) <- runState "hi" $ do+ stateM $ \s -> do+ effs0 <- getEffectReps+ -- Trigger reallocation of the internal array in Storage.+ _ <- evalState () $ pure ()+ effs1 <- getEffectReps+ when (effs0 == effs1) $ do+ U.assertFailure "Internal array was not reallocated"+ pure (s, s ++ "!!!") U.assertEqual "correct a" "hi" a U.assertEqual "correct b" "hi!!!" b+ where+ getEffectReps = unsafeEff $ \es -> sdEffects . stData <$> readIORef' (envStorage es) test_deepStack :: Assertion test_deepStack = runEff $ do@@ -52,12 +67,14 @@ test_exceptions :: Assertion test_exceptions = runEff $ do- testTry "exceptions" E.try- testCatch "exceptions" E.catch- testTry "lifted-base" LE.try- testCatch "lifted-base" LE.catch- testTry "unliftio" UE.try- testCatch "unliftio" UE.catch+ testTry "exceptions" C.try+ testCatch "exceptions" C.catch+ testTry "safe-exceptions" Safe.try+ testCatch "safe-exceptions" Safe.catch+ testTry "lifted-base" LE.try+ testCatch "lifted-base" LE.catch+ testTry "unliftio" UE.try+ testCatch "unliftio" UE.catch where testTry :: String@@ -82,7 +99,7 @@ action :: State Int :> es => Eff es () action = do modify @Int (+1)- _ <- E.throwM U.Ex+ _ <- C.throwM U.Ex modify @Int (+2) test_localEffects :: Assertion@@ -117,7 +134,7 @@ runHasInt :: Int -> Eff (HasInt : es) a -> Eff es a runHasInt n = -- reinterpret with redundant local effects- reinterpret (evalState () . evalState n . evalState True) $ \_ -> \case+ reinterpret_ (evalState () . evalState n . evalState True) $ \case GetInt -> get PutInt i -> put i
tests/TimeoutTests.hs view
@@ -6,7 +6,7 @@ import Effectful import Effectful.Timeout-import qualified Utils as U+import Utils qualified as U timeoutTests :: TestTree timeoutTests = testGroup "Timeout"
tests/UnliftTests.hs view
@@ -1,12 +1,14 @@ module UnliftTests (unliftTests) where -import Control.Exception import Test.Tasty import Test.Tasty.HUnit-import qualified UnliftIO.Async as A+import UnliftIO.Async qualified as A import Effectful-import qualified Utils as U+import Effectful.Concurrent.Async qualified as E+import Effectful.Dispatch.Dynamic+import Effectful.State.Static.Local+import Utils qualified as U unliftTests :: TestTree unliftTests = testGroup "Unlift"@@ -22,6 +24,7 @@ , testCase "Uses in same thread" test_persistentSameThread , testCase "Uses in multiple threads" test_persistentMultipleThreads ]+ , testCase "Unlifting functions work correctly" test_unliftingFunctions ] test_threadStrategy :: Assertion@@ -34,32 +37,32 @@ test_seqUnliftInNewThread :: Assertion test_seqUnliftInNewThread = runEff $ do- assertThrowsUnliftError "InvalidUseOfSeqUnlift error" $ do+ U.assertThrowsErrorCall "InvalidUseOfSeqUnlift error" $ do withEffToIO SeqUnlift $ \runInIO -> do inThread $ runInIO $ return () test_ephemeralInvalid :: Assertion test_ephemeralInvalid = runEff $ do- assertThrowsUnliftError "InvalidNumberOfUses error" $ do+ U.assertThrowsErrorCall "InvalidNumberOfUses error" $ do withEffToIO (ConcUnlift Ephemeral $ Limited 0) $ \_ -> return () test_ephemeralSameThread :: Assertion test_ephemeralSameThread = runEff $ do- assertThrowsUnliftError "ExceededNumberOfUses error" $ do+ U.assertThrowsErrorCall "ExceededNumberOfUses error" $ do withEffToIO (ConcUnlift Ephemeral $ Limited 1) $ \runInIO -> inThread $ do runInIO $ return () runInIO $ return () test_ephemeralMultipleThreads :: Assertion test_ephemeralMultipleThreads = runEff $ do- assertThrowsUnliftError "ExceededNumberOfUses error" $ do+ U.assertThrowsErrorCall "ExceededNumberOfUses error" $ do withEffToIO (ConcUnlift Ephemeral $ Limited 1) $ \runInIO -> do inThread $ runInIO $ return () inThread $ runInIO $ return () test_persistentInvalid :: Assertion test_persistentInvalid = runEff $ do- assertThrowsUnliftError "InvalidNumberOfThreads error" $ do+ U.assertThrowsErrorCall "InvalidNumberOfThreads error" $ do withEffToIO (ConcUnlift Persistent $ Limited 0) $ \_ -> return () test_persistentSameThread :: Assertion@@ -70,18 +73,85 @@ test_persistentMultipleThreads :: Assertion test_persistentMultipleThreads = runEff $ do- assertThrowsUnliftError "ExceededNumberOfThreads error" $ do+ U.assertThrowsErrorCall "ExceededNumberOfThreads error" $ do withEffToIO (ConcUnlift Persistent $ Limited 1) $ \runInIO -> do inThread $ runInIO $ return () inThread $ runInIO $ return () +test_unliftingFunctions :: Assertion+test_unliftingFunctions = runEff . E.runConcurrent $ do+ testFork "runFork1" runFork1+ testFork "runFork2" runFork2+ testFork "runFork3" runFork3+ testFork "runFork4" runFork4+ testFork "runFork5" runFork5+ where+ testFork description runFork = do+ a <- runFork . send $ ForkWithUnmask $ \unmask -> do+ evalState @Int 0 $ raiseWith SeqUnlift $ \unlift -> do+ unlift $ modify @Int (+1)+ unmask . unlift $ modify @Int (+2)+ unlift $ modify @Int (+4)+ unmask . unlift $ modify @Int (+8)+ unlift $ U.assertEqual (description ++ ": correct state") 15 =<< get @Int+ E.waitCatch a >>= \case+ Right () -> pure ()+ Left err -> U.assertFailure $ description ++ ": " ++ show err++data Fork :: Effect where+ ForkWithUnmask :: ((forall a. m a -> m a) -> m r) -> Fork m (A.Async r)+type instance DispatchOf Fork = Dynamic++-- | Uses 'localUnliftIO' and 'withLiftMapIO'.+runFork1 :: IOE :> es => Eff (Fork : es) a -> Eff es a+runFork1 = interpret $ \env -> \case+ ForkWithUnmask m -> do+ withLiftMapIO env $ \liftMap -> do+ localUnliftIO env strategy $ \unlift -> do+ A.asyncWithUnmask $ \unmask -> unlift $ m $ liftMap unmask+ where+ strategy = ConcUnlift Ephemeral $ Limited 1++-- | Uses 'localUnlift' and 'withLiftMap'.+runFork2 :: (IOE :> es, E.Concurrent :> es) => Eff (Fork : es) a -> Eff es a+runFork2 = interpret $ \env -> \case+ ForkWithUnmask m -> do+ withLiftMap env $ \liftMap -> do+ localUnlift env strategy $ \unlift -> do+ E.asyncWithUnmask $ \unmask -> unlift $ m $ liftMap unmask+ where+ strategy = ConcUnlift Ephemeral $ Limited 1++-- | Uses 'localLiftUnliftIO'.+runFork3 :: IOE :> es => Eff (Fork : es) a -> Eff es a+runFork3 = interpret $ \env -> \case+ ForkWithUnmask m -> do+ localLiftUnliftIO env strategy $ \lift unlift -> do+ A.asyncWithUnmask $ \unmask -> unlift $ m $ lift . unmask . unlift+ where+ strategy = ConcUnlift Persistent $ Limited 1++-- | Uses 'localLiftUnlift'.+runFork4 :: (IOE :> es, E.Concurrent :> es) => Eff (Fork : es) a -> Eff es a+runFork4 = interpret $ \env -> \case+ ForkWithUnmask m -> do+ localLiftUnlift env strategy $ \lift unlift -> do+ E.asyncWithUnmask $ \unmask -> unlift $ m $ lift . unmask . unlift+ where+ strategy = ConcUnlift Persistent $ Limited 1++-- | Uses 'localLift' and 'localUnlift'.+runFork5 :: (IOE :> es, E.Concurrent :> es) => Eff (Fork : es) a -> Eff es a+runFork5 = interpret $ \env -> \case+ ForkWithUnmask m -> do+ localLift env strategy $ \lift -> do+ localUnlift env strategy $ \unlift -> do+ E.asyncWithUnmask $ \unmask -> unlift $ m $ lift . unmask . unlift+ where+ strategy = ConcUnlift Persistent $ Limited 1+ ---------------------------------------- -- Helpers--assertThrowsUnliftError- :: IOE :> es- => String -> Eff es a -> Eff es ()-assertThrowsUnliftError err = U.assertThrows err (\ErrorCall{} -> True) inThread :: IO a -> IO a inThread k = A.async k >>= A.wait
tests/Utils.hs view
@@ -3,14 +3,15 @@ , assertEqual , assertFailure , assertThrows+ , assertThrowsErrorCall , Ex(..) ) where -import Control.Monad.Catch import GHC.Stack-import qualified Test.Tasty.HUnit as T+import Test.Tasty.HUnit qualified as T import Effectful+import Effectful.Exception assertBool :: (HasCallStack, IOE :> es) => String -> Bool -> Eff es () assertBool msg p = liftIO $ T.assertBool msg p@@ -37,5 +38,11 @@ (k >> liftIO (T.assertFailure msg)) pure -data Ex = Ex deriving (Eq, Show)+assertThrowsErrorCall+ :: IOE :> es+ => String -> Eff es a -> Eff es ()+assertThrowsErrorCall err = assertThrows err (\ErrorCall{} -> True)++data Ex = Ex+ deriving stock (Eq, Show) instance Exception Ex