packages feed

cleff 0.2.1.0 → 0.3.0.0

raw patch · 19 files changed

+529/−266 lines, 19 filesdep −QuickCheckdep ~basedep ~containersdep ~microlensPVP ok

version bump matches the API change (PVP)

Dependencies removed: QuickCheck

Dependency ranges changed: base, containers, microlens, template-haskell, th-abstraction, transformers-base, unliftio

API changes (from Hackage documentation)

- Cleff.Fail: instance (Cleff.Fail.Fail Cleff.Internal.Effect.:> es) => Control.Monad.Fail.MonadFail (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Effect.:> es) => Control.Monad.Base.MonadBase GHC.Types.IO (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Effect.:> es) => Control.Monad.Catch.MonadCatch (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Effect.:> es) => Control.Monad.Catch.MonadMask (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Effect.:> es) => Control.Monad.Catch.MonadThrow (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Effect.:> es) => Control.Monad.IO.Class.MonadIO (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Effect.:> es) => Control.Monad.IO.Unlift.MonadUnliftIO (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Effect.:> es) => Control.Monad.Primitive.PrimMonad (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Effect.:> es) => Control.Monad.Trans.Control.MonadBaseControl GHC.Types.IO (Cleff.Internal.Monad.Eff es)
- Cleff.Internal.Effect: infix 0 :>>
- Cleff.Internal.Effect: infixr 0 ~>
- Cleff.Internal.Effect: infixr 5 ++
- Cleff.Internal.Effect: type (:>) = Elem
- Cleff.Internal.Effect: type Effect = (Type -> Type) -> Type -> Type
- Cleff.Internal.Effect: type (f :: k -> Type) ~> (g :: k -> Type) = forall (a :: k). () => f a -> g a
- Cleff.Internal.Effect: type family (xs :: [a]) ++ (ys :: [a]) :: [a]
- Cleff.Mask: [BracketOnError] :: m a -> (a -> m c) -> (a -> m b) -> Mask m b
- Cleff.Mask: [Bracket] :: m a -> (a -> m c) -> (a -> m b) -> Mask m b
+ Cleff.Fail: instance (Cleff.Fail.Fail Cleff.Internal.Monad.:> es) => Control.Monad.Fail.MonadFail (Cleff.Internal.Monad.Eff es)
+ Cleff.Fresh: freshEnumToState :: Enum a => Eff (Fresh a : es) ~> Eff (State a : es)
+ Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Monad.:> es) => Control.Monad.Base.MonadBase GHC.Types.IO (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Monad.:> es) => Control.Monad.Catch.MonadCatch (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Monad.:> es) => Control.Monad.Catch.MonadMask (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Monad.:> es) => Control.Monad.Catch.MonadThrow (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Monad.:> es) => Control.Monad.IO.Class.MonadIO (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Monad.:> es) => Control.Monad.IO.Unlift.MonadUnliftIO (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Monad.:> es) => Control.Monad.Primitive.PrimMonad (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Base: instance (Cleff.Internal.Base.IOE Cleff.Internal.Monad.:> es) => Control.Monad.Trans.Control.MonadBaseControl GHC.Types.IO (Cleff.Internal.Monad.Eff es)
+ Cleff.Internal.Monad: infix 0 :>>
+ Cleff.Internal.Monad: infixr 0 ~>
+ Cleff.Internal.Monad: infixr 5 ++
+ Cleff.Internal.Monad: type (:>) = Elem
+ Cleff.Internal.Monad: type Effect = (Type -> Type) -> Type -> Type
+ Cleff.Internal.Monad: type (f :: k -> Type) ~> (g :: k -> Type) = forall (a :: k). () => f a -> g a
+ Cleff.Internal.Monad: type family (xs :: [a]) ++ (ys :: [a]) :: [a]
+ Cleff.Mask: [OnException] :: m a -> m b -> Mask m a
+ Cleff.Mask: bracketOnError_ :: Mask :> es => Eff es a -> Eff es c -> (a -> Eff es b) -> Eff es b
+ Cleff.Mask: onException :: Mask :> es => Eff es a -> Eff es b -> Eff es a
- Cleff.Error: catchError :: Error e_afOB :> es_afTz => Eff es_afTz a_afOA -> (e_afOB -> Eff es_afTz a_afOA) -> Eff es_afTz a_afOA
+ Cleff.Error: catchError :: Error e :> es => Eff es a -> (e -> Eff es a) -> Eff es a
- Cleff.Error: throwError :: Error e_afOw :> es_afTC => e_afOw -> Eff es_afTC a_afOy
+ Cleff.Error: throwError :: Error e :> es => e -> Eff es a
- Cleff.Fresh: fresh :: Fresh u_akl5 :> es_aklO => Eff es_aklO u_akl5
+ Cleff.Fresh: fresh :: Fresh u :> es => Eff es u
- Cleff.Input: input :: Input i_ajZM :> es_ak0v => Eff es_ak0v i_ajZM
+ Cleff.Input: input :: Input i :> es => Eff es i
- Cleff.Mask: bracket :: Mask :> es_ahLc => Eff es_ahLc a_XhId -> (a_XhId -> Eff es_ahLc c_XhIf) -> (a_XhId -> Eff es_ahLc b_ahIe) -> Eff es_ahLc b_ahIe
+ Cleff.Mask: bracket :: Mask :> es => Eff es a -> (a -> Eff es c) -> (a -> Eff es b) -> Eff es b
- Cleff.Mask: bracketOnError :: Mask :> es_ahL8 => Eff es_ahL8 a_XhIh -> (a_XhIh -> Eff es_ahL8 c_XhIj) -> (a_XhIh -> Eff es_ahL8 b_ahIi) -> Eff es_ahL8 b_ahIi
+ Cleff.Mask: bracketOnError :: Mask :> es => Eff es a -> (a -> Eff es c) -> (a -> Eff es b) -> Eff es b
- Cleff.Mask: mask :: Mask :> es_ahLi => ((~>) (Eff es_ahLi) (Eff es_ahLi) -> Eff es_ahLi a_ahI8) -> Eff es_ahLi a_ahI8
+ Cleff.Mask: mask :: Mask :> es => ((Eff es ~> Eff es) -> Eff es a) -> Eff es a
- Cleff.Mask: uninterruptibleMask :: Mask :> es_ahLg => ((~>) (Eff es_ahLg) (Eff es_ahLg) -> Eff es_ahLg a_ahIa) -> Eff es_ahLg a_ahIa
+ Cleff.Mask: uninterruptibleMask :: Mask :> es => ((Eff es ~> Eff es) -> Eff es a) -> Eff es a
- Cleff.Output: output :: Output o_alnz :> es_alol => o_alnz -> Eff es_alol ()
+ Cleff.Output: output :: Output o :> es => o -> Eff es ()
- Cleff.Reader: ask :: Reader r_ainM :> es_aipp => Eff es_aipp r_ainM
+ Cleff.Reader: ask :: Reader r :> es => Eff es r
- Cleff.Reader: local :: Reader r_ainO :> es_aipm => (r_ainO -> r_ainO) -> Eff es_aipm a_ainQ -> Eff es_aipm a_ainQ
+ Cleff.Reader: local :: Reader r :> es => (r -> r) -> Eff es a -> Eff es a
- Cleff.State: get :: State s_aiUI :> es_aiWU => Eff es_aiWU s_aiUI
+ Cleff.State: get :: State s :> es => Eff es s
- Cleff.State: put :: State s_aiUK :> es_aiWS => s_aiUK -> Eff es_aiWS ()
+ Cleff.State: put :: State s :> es => s -> Eff es ()
- Cleff.State: state :: State s_aiUM :> es_aiWQ => (s_aiUM -> (a_aiUN, s_aiUM)) -> Eff es_aiWQ a_aiUN
+ Cleff.State: state :: State s :> es => (s -> (a, s)) -> Eff es a
- Cleff.Trace: trace :: Trace :> es_alKz => String -> Eff es_alKz ()
+ Cleff.Trace: trace :: Trace :> es => String -> Eff es ()
- Cleff.Writer: listen :: Writer w_akDy :> es_akEZ => Eff es_akEZ a_XkDy -> Eff es_akEZ (a_XkDy, w_akDy)
+ Cleff.Writer: listen :: Writer w :> es => Eff es a -> Eff es (a, w)
- Cleff.Writer: tell :: Writer w_akDu :> es_akF1 => w_akDu -> Eff es_akF1 ()
+ Cleff.Writer: tell :: Writer w :> es => w -> Eff es ()

Files

CHANGELOG.md view
@@ -1,12 +1,25 @@ # Changelog for `cleff` +## Unreleased++### Changed++- **[BREAKING]** Introduces an `OnException` primitive for `Mask` that replaces `Bracket` and `BracketOnError`+  (note that this only affects the effect datatype; there is still `bracket` and `bracketOnError` *functions* with the same semantics)+- `runError` and `mapError` are slightly (but observably) faster now++### Added++- `freshEnumToState` for `Fresh`+- `onException` and `bracketOnError_` for `Mask`+ ## 0.2.1.0 (2022-02-13)  ### Added  - Lifted convenience instances of `Bounded`, `Num`, `Fractional`, `Floating` and `IsString` for `Eff` - `MonadZip` instance from the `MonadComprehensions` extension for `Eff`-- `freshEnumToState` and `runFreshAtomicCounter` for `Fresh`+- `runFreshAtomicCounter` for `Fresh` - `inputToReader`, `mapInput` and `bindInput` for `Input` - `mapOutput` and `bindOutput` for `Output` - `runStateIORef`, `runStateMVar` and `runStateTVar` for `State`
cleff.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           cleff-version:        0.2.1.0+version:        0.3.0.0 synopsis:       Fast and concise extensible effects description:    Please see the README on GitHub at <https://github.com/re-xyr/cleff#readme> category:       Control, Effect, Language@@ -45,7 +45,6 @@       Cleff.Input       Cleff.Internal.Any       Cleff.Internal.Base-      Cleff.Internal.Effect       Cleff.Internal.Instances       Cleff.Internal.Interpret       Cleff.Internal.Monad@@ -65,6 +64,8 @@       BlockArguments       ConstraintKinds       DataKinds+      DeriveAnyClass+      DerivingStrategies       DerivingVia       EmptyCase       FlexibleContexts@@ -77,10 +78,10 @@       NoStarIsType       PatternSynonyms       PolyKinds-      QuantifiedConstraints       RankNTypes       RoleAnnotations       ScopedTypeVariables+      StandaloneDeriving       TemplateHaskell       TupleSections       TypeApplications@@ -88,21 +89,20 @@       TypeOperators       UndecidableInstances       UnicodeSyntax-      ViewPatterns   ghc-options: -Wall -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wpartial-fields -Wunused-type-patterns -Wmissing-export-lists   build-depends:       atomic-primops ==0.8.*-    , base >=4.12 && <5-    , containers ==0.6.*+    , base >=4.12 && <4.17+    , containers >=0.5 && <0.7     , exceptions ==0.10.*-    , microlens >=0.4.9 && <0.5+    , microlens >=0.1 && <0.5     , monad-control >=1 && <1.1     , primitive >=0.6 && <0.8     , rec-smallarray ==0.1.*-    , template-haskell >=2.14 && <3-    , th-abstraction >=0.2.11 && <0.5-    , transformers-base >=0.4.5 && <0.5-    , unliftio >=0.2.8 && <0.3+    , template-haskell >=2.14 && <2.19+    , th-abstraction >=0.2 && <0.5+    , transformers-base >=0.1 && <0.5+    , unliftio >=0.2.4 && <0.3   if flag(dynamic-ioe)     cpp-options: -DDYNAMIC_IOE   default-language: Haskell2010@@ -122,6 +122,8 @@       BlockArguments       ConstraintKinds       DataKinds+      DeriveAnyClass+      DerivingStrategies       DerivingVia       EmptyCase       FlexibleContexts@@ -134,10 +136,10 @@       NoStarIsType       PatternSynonyms       PolyKinds-      QuantifiedConstraints       RankNTypes       RoleAnnotations       ScopedTypeVariables+      StandaloneDeriving       TemplateHaskell       TupleSections       TypeApplications@@ -145,24 +147,22 @@       TypeOperators       UndecidableInstances       UnicodeSyntax-      ViewPatterns-      DeriveAnyClass   ghc-options: -Wall -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wpartial-fields -Wunused-type-patterns -threaded -rtsopts -with-rtsopts=-N   build-depends:       atomic-primops ==0.8.*-    , base >=4.12 && <5+    , base >=4.12 && <4.17     , cleff-    , containers ==0.6.*+    , containers >=0.5 && <0.7     , exceptions ==0.10.*     , extra-    , microlens >=0.4.9 && <0.5+    , microlens >=0.1 && <0.5     , monad-control >=1 && <1.1     , primitive >=0.6 && <0.8     , rec-smallarray ==0.1.*-    , template-haskell >=2.14 && <3-    , th-abstraction >=0.2.11 && <0.5-    , transformers-base >=0.4.5 && <0.5-    , unliftio >=0.2.8 && <0.3+    , template-haskell >=2.14 && <2.19+    , th-abstraction >=0.2 && <0.5+    , transformers-base >=0.1 && <0.5+    , unliftio >=0.2.4 && <0.3   if flag(dynamic-ioe)     cpp-options: -DDYNAMIC_IOE   default-language: Haskell2010@@ -186,6 +186,8 @@       BlockArguments       ConstraintKinds       DataKinds+      DeriveAnyClass+      DerivingStrategies       DerivingVia       EmptyCase       FlexibleContexts@@ -198,10 +200,10 @@       NoStarIsType       PatternSynonyms       PolyKinds-      QuantifiedConstraints       RankNTypes       RoleAnnotations       ScopedTypeVariables+      StandaloneDeriving       TemplateHaskell       TupleSections       TypeApplications@@ -209,28 +211,24 @@       TypeOperators       UndecidableInstances       UnicodeSyntax-      ViewPatterns-      DeriveAnyClass-      DeriveGeneric   ghc-options: -Wall -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wpartial-fields -Wunused-type-patterns -threaded -rtsopts -with-rtsopts=-N   build-depends:-      QuickCheck-    , atomic-primops ==0.8.*-    , base >=4.12 && <5+      atomic-primops ==0.8.*+    , base >=4.12 && <4.17     , cleff-    , containers ==0.6.*+    , containers >=0.5 && <0.7     , exceptions ==0.10.*     , extra     , hspec     , lifted-base-    , microlens >=0.4.9 && <0.5+    , microlens >=0.1 && <0.5     , monad-control >=1 && <1.1     , primitive >=0.6 && <0.8     , rec-smallarray ==0.1.*-    , template-haskell >=2.14 && <3-    , th-abstraction >=0.2.11 && <0.5-    , transformers-base >=0.4.5 && <0.5-    , unliftio >=0.2.8 && <0.3+    , template-haskell >=2.14 && <2.19+    , th-abstraction >=0.2 && <0.5+    , transformers-base >=0.1 && <0.5+    , unliftio >=0.2.4 && <0.3   if flag(dynamic-ioe)     cpp-options: -DDYNAMIC_IOE   default-language: Haskell2010
src/Cleff.hs view
@@ -9,7 +9,7 @@ -- This library implements an /extensible effects system/, where sets of monadic actions ("effects") are encoded as -- datatypes, tracked at the type level and can have multiple different implementations. This means you can swap out -- implementations of certain monadic actions in mock tests or in different environments. The notion of "effect" is--- general here: it can be an 'IO'-performing side effect, or just obtaining the value of a static global environment.+-- general here: it can be an 'IO'-performing side effect, or just reading the value of a static global environment. -- -- In particular, this library consists of --@@ -20,43 +20,77 @@ -- * Combinators for defining new effects and interpreting them /on your own/. These effects can be translated in terms --   of other already existing effects, or into operations in the 'IO' monad. ----- So, this library allows you to do two things:+-- In terms of structuring your application, this library helps you to do two things: -- -- * __Effect management:__ The 'Eff' monad tracks what effects are used explicitly at the type level, therefore you---   are able to be certain about what effects are involved in each function.--- * __Effect decoupling:__ You can decouple the implementation of the effects from your application and swap them---   easily.+--   are able to enforce what effects are involved in each function, and avoid accidentally introduced behaviors.+-- * __Effect decoupling:__ You can swap between the implementations of the effects in your application easily,+--   so you can refactor and test your applications with less clutter. module Cleff   ( -- * Using effects-    Eff, (:>), (:>>), Effect, IOE-  , -- ** Running effects+    Eff+  , (:>)+  , (:>>)+  , Effect+  , IOE+    -- ** Running effects     -- $runningEffects-    runPure, runIOE-  , -- * Defining effects+  , runPure+  , runIOE+    -- * Defining effects     -- $definingEffects-    send, sendVia, makeEffect, makeEffect_-  , -- * Trivial effects handling-    raise, raiseN, inject, subsume, subsumeN, KnownList, Subset-  , -- * Interpreting effects+  , send+  , sendVia+  , makeEffect+  , makeEffect_+    -- * Trivial effects handling+  , raise+  , raiseN+  , inject+  , subsume+  , subsumeN+  , KnownList+  , Subset+    -- * Interpreting effects     -- $interpretingEffects-    Handler, interpret, reinterpret, reinterpret2, reinterpret3, reinterpretN, interpose, impose, imposeN-  , -- ** Interpreting in terms of 'IO'-    HandlerIO, interpretIO-  , -- ** Translating effects-    Translator, transform, translate-  , -- ** Transforming interpreters-    raiseUnder, raiseNUnder, raiseUnderN, raiseNUnderN-  , -- * Combinators for interpreting higher order effects+  , Handler+  , interpret+  , reinterpret+  , reinterpret2+  , reinterpret3+  , reinterpretN+  , interpose+  , impose+  , imposeN+    -- ** Interpreting in terms of 'IO'+  , HandlerIO+  , interpretIO+    -- ** Translating effects+  , Translator+  , transform+  , translate+    -- ** Transforming interpreters+  , raiseUnder+  , raiseNUnder+  , raiseUnderN+  , raiseNUnderN+    -- * Combinators for interpreting higher order effects     -- $higherOrderEffects-    Handling, toEff, toEffWith, withFromEff-  , -- ** Interpreting 'IO'-related higher order effects-    withToIO, fromIO-  , -- * Miscellaneous-    type (~>), type (++), MonadIO (..), MonadUnliftIO (..)+  , Handling+  , toEff+  , toEffWith+  , withFromEff+    -- ** Interpreting 'IO'-related higher order effects+  , withToIO+  , fromIO+    -- * Miscellaneous+  , type (~>)+  , type (++)+  , MonadIO (..)+  , MonadUnliftIO (..)   ) where  import           Cleff.Internal.Base-import           Cleff.Internal.Effect import           Cleff.Internal.Instances () import           Cleff.Internal.Interpret import           Cleff.Internal.Monad@@ -64,21 +98,21 @@ import           UnliftIO                 (MonadIO (liftIO), MonadUnliftIO (withRunInIO))  -- $runningEffects--- To run an effect @T@, we should use an /interpreter/ of @T@, which is a function that has type like this:+-- To run an effect @T@, we should use an /interpreter/ of @T@, which is a function that has a type like this: -- -- @ -- runT :: 'Eff' (T ': es) a -> 'Eff' es a -- @ -- -- Such an interpreter provides an implementation of @T@ and eliminates @T@ from the effect stack. All builtin effects--- in @cleff@ have interpreters coming together with them.+-- in @cleff@ have interpreters out of the box in their respective modules. -- -- By applying interpreters to an 'Eff' computation, you can eventually obtain an /end computation/, where there are no--- more effects present on the effect stack. There are two kinds of end computations:+-- more effects to be interpreted on the effect stack. There are two kinds of end computations: ----- * A /pure computation/ with the type @'Eff' '[] a@, which you can obtain the value via 'Cleff.runPure'; or,--- * An /impure computation/ with type @'Eff' '['Cleff.IOE'] a@ that can be transformed into an IO computation via---   'Cleff.runIOE'.+-- * A /pure computation/ with the type @'Eff' '[] a@, which you can obtain the value via 'runPure'; or,+-- * An /impure computation/ with type @'Eff' '['IOE'] a@ that can be unwrapped into an IO computation via+--   'runIOE'.  -- $definingEffects -- An effect should be defined as a GADT and have the kind 'Effect'. Each operation in the effect is a constructor of
src/Cleff/Error.hs view
@@ -9,51 +9,78 @@ module Cleff.Error   ( -- * Effect     Error (..)-  , -- * Operations-    throwError, catchError, fromEither, fromException, fromExceptionVia, fromExceptionEff, fromExceptionEffVia,-    note, catchErrorJust, catchErrorIf, handleError, handleErrorJust, handleErrorIf, tryError, tryErrorJust-  , -- * Interpretations-    runError, mapError+    -- * Operations+  , throwError+  , catchError+    -- ** Other ways of throwing errors+  , fromEither+  , fromException+  , fromExceptionVia+  , fromExceptionEff+  , fromExceptionEffVia+  , note+    -- ** Other ways of handling errors+  , catchErrorJust+  , catchErrorIf+  , handleError+  , handleErrorJust+  , handleErrorIf+  , tryError+  , tryErrorJust+    -- * Interpretations+  , runError+  , mapError   ) where  import           Cleff import           Cleff.Internal.Any import           Cleff.Internal.Base-import           Control.Exception   (Exception)-import           Data.Bool           (bool)-import           Data.Unique         (Unique, hashUnique, newUnique)-import qualified UnliftIO.Exception  as Exc+import           Control.Exception    (Exception)+import           Data.Atomics.Counter (AtomicCounter, incrCounter, newCounter)+import           Data.Bool            (bool)+import           System.IO.Unsafe     (unsafePerformIO)+import qualified UnliftIO.Exception   as Exc  -- * Effect --- | An effect capable of breaking out of current control flow by raising an exceptional value @e@. This effect roughly--- corresponds to the @MonadError@ typeclass and @ExceptT@ monad transformer in @mtl@.+-- | An effect capable of breaking out of current control flow by throwing an error of type @e@, and handling the+-- errors thrown from computations. This effect roughly corresponds to the @MonadError@ typeclass and @ExceptT@ monad+-- transformer in @mtl@. data Error e :: Effect where   ThrowError :: e -> Error e m a   CatchError :: m a -> (e -> m a) -> Error e m a  -- * Operations -makeEffect ''Error+makeEffect_ ''Error +-- | Throw an error in the current computation.+throwError :: Error e :> es => e -> Eff es a++-- | Handle an error if one is thrown from a computation, and then return to normal control flow.+catchError :: Error e :> es+  => Eff es a -- ^ The computation that may throw errors+  -> (e -> Eff es a) -- ^ The handler that is called when an error is thrown+  -> Eff es a+ -- | Lift an 'Either' value into the 'Error' effect. fromEither :: Error e :> es => Either e a -> Eff es a fromEither = either throwError pure  -- | Lift exceptions generated by an 'IO' computation into the 'Error' effect.-fromException :: ∀ e es a. (Exc.Exception e, '[Error e, IOE] :>> es) => IO a -> Eff es a+fromException :: ∀ e es a. (Exception e, '[Error e, IOE] :>> es) => IO a -> Eff es a fromException m = Exc.catch (liftIO m) (throwError @e)  -- | Like 'fromException', but allows to transform the exception into another error type.-fromExceptionVia :: (Exc.Exception ex, '[Error er, IOE] :>> es) => (ex -> er) -> IO a -> Eff es a+fromExceptionVia :: (Exception ex, '[Error er, IOE] :>> es) => (ex -> er) -> IO a -> Eff es a fromExceptionVia f m = Exc.catch (liftIO m) (throwError . f)  -- | Lift exceptions generated by an 'Eff' computation into the 'Error' effect.-fromExceptionEff :: ∀ e es a. (Exc.Exception e, '[Error e, IOE] :>> es) => Eff es a -> Eff es a+fromExceptionEff :: ∀ e es a. (Exception e, '[Error e, IOE] :>> es) => Eff es a -> Eff es a fromExceptionEff m = withRunInIO \unlift -> Exc.catch (unlift m) (unlift . throwError @e)  -- | Like 'fromExceptionEff', but allows to transform the exception into another error type.-fromExceptionEffVia :: (Exc.Exception ex, '[Error er, IOE] :>> es) => (ex -> er) -> Eff es a -> Eff es a+fromExceptionEffVia :: (Exception ex, '[Error er, IOE] :>> es) => (ex -> er) -> Eff es a -> Eff es a fromExceptionEffVia f m = withRunInIO \unlift -> Exc.catch (unlift m) (unlift . throwError . f)  -- | Try to extract a value from 'Maybe', throw an error otherwise.@@ -93,27 +120,37 @@  -- * Interpretations --- | Exception wrapper used in 'runError' in order not to conflate error types with exception types.-data ErrorExc = ErrorExc !Unique Any+type ExcUid = Int -instance Exception ErrorExc+-- | Exception wrapper used in 'runError' in order not to conflate error types with exception types.+data ErrorExc = ErrorExc {-# UNPACK #-} !ExcUid Any+  deriving anyclass (Exception)  instance Show ErrorExc where   showsPrec _ (ErrorExc uid _) =-    ("Cleff.Error.runError: Escaped error (error UID hash: " <>) . shows (hashUnique uid) . ("). This is possibly due \+    ("Cleff.Error.runError: Escaped error (error UID: " <>) . shows uid . ("). This is possibly due \     \to trying to 'throwError' in a forked thread, or trying to 'wait' on an error-throwing 'Async' computation out \     \of the effect scope where it is created. Refer to the haddock of 'runError' for details on the caveats. If all \     \those shenanigans mentioned or other similar ones seem unlikely, please report this as a bug." <>) -catch' :: ∀ e m a. MonadUnliftIO m => Unique -> m a -> (e -> m a) -> m a-catch' eid m h = m `Exc.catch` \ex@(ErrorExc eid' e) -> if eid == eid' then h (fromAny e) else Exc.throwIO ex+catch' :: ∀ e m a. MonadUnliftIO m => ExcUid -> m a -> (e -> m a) -> m a+catch' eid m h = m `Exc.catch` \ex@(ErrorExc eid' e) ->+  if eid == eid' then h (fromAny e) else Exc.throwIO ex {-# INLINE catch' #-} -try' :: ∀ e m a. MonadUnliftIO m => Unique -> m a -> m (Either e a)+try' :: ∀ e m a. MonadUnliftIO m => ExcUid -> m a -> m (Either e a) try' eid m = catch' eid (Right <$> m) (pure . Left) {-# INLINE try' #-} -errorHandler :: Unique -> Handler (Error e) (IOE ': es)+excUidSource :: AtomicCounter+excUidSource = unsafePerformIO (newCounter 0)+{-# NOINLINE excUidSource #-}++newExcUid :: IO ExcUid+newExcUid = incrCounter 1 excUidSource+{-# INLINE newExcUid #-}++errorHandler :: ExcUid -> Handler (Error e) (IOE ': es) errorHandler eid = \case   ThrowError e     -> Exc.throwIO $ ErrorExc eid (toAny e)   CatchError m' h' -> withToIO \toIO -> liftIO $ catch' eid (toIO m') (toIO . h')@@ -121,7 +158,9 @@  -- | Run an 'Error' effect. ----- __Caveat__: 'runError' is implemented with 'Exc.Exception's therefore inherits some of its unexpected behaviors.+-- === Caveats+--+-- 'runError' is implemented with 'Exc.Exception's therefore inherits some of its unexpected behaviors. -- Errors thrown in forked threads will /not/ be directly caught by 'catchError's in the parent thread. Instead it will -- incur an exception, and we won't be quite able to display the details of that exception properly at that point. -- Therefore please properly handle the errors in the forked threads separately.@@ -132,7 +171,7 @@ -- will again not be caught. The best choice is /not to pass @Async@ values around randomly/. runError :: ∀ e es a. Eff (Error e ': es) a -> Eff es (Either e a) runError m = thisIsPureTrustMe do-  eid <- liftIO newUnique+  eid <- liftIO newExcUid   try' eid $ reinterpret (errorHandler eid) m {-# INLINE runError #-} @@ -141,7 +180,7 @@ mapError f = thisIsPureTrustMe . reinterpret \case   ThrowError e   -> throwError $ f e   CatchError m h -> do-    eid <- liftIO newUnique+    eid <- liftIO newExcUid     res <- try' @e eid $ toEffWith (errorHandler eid) m     case res of       Left e  -> toEff (h e)
src/Cleff/Fail.hs view
@@ -9,8 +9,9 @@ module Cleff.Fail   ( -- * Effect     Fail (..)-  , -- * Interpretations-    runFail, runFailIO+    -- * Interpretations+  , runFail+  , runFailIO   ) where  import           Cleff
src/Cleff/Fresh.hs view
@@ -8,10 +8,13 @@ module Cleff.Fresh   ( -- * Effect     Fresh (..)-  , -- * Operations-    fresh-  , -- * Interpretations-    freshIntToState, runFreshAtomicCounter, runFreshUnique+    -- * Operations+  , fresh+    -- * Interpretations+  , freshIntToState+  , freshEnumToState+  , runFreshAtomicCounter+  , runFreshUnique   ) where  import           Cleff@@ -28,7 +31,10 @@  -- * Operations -makeEffect ''Fresh+makeEffect_ ''Fresh++-- | Obtain a fresh unique value.+fresh :: Fresh u :> es => Eff es u  -- * Interpretations 
src/Cleff/Input.hs view
@@ -8,10 +8,16 @@ module Cleff.Input   ( -- * Effect     Input (..)-  , -- * Operations-    input, inputs-  , -- * Interpretations-    runInputConst, inputToListState, inputToReader, runInputEff, mapInput, bindInput+    -- * Operations+  , input+  , inputs+    -- * Interpretations+  , runInputConst+  , inputToListState+  , inputToReader+  , runInputEff+  , mapInput+  , bindInput   ) where  import           Cleff@@ -26,7 +32,10 @@  -- * Operations -makeEffect ''Input+makeEffect_ ''Input++-- | Read an input value from an input source.+input :: Input i :> es => Eff es i  -- | Apply a function to the result of 'input'. inputs :: Input i :> es => (i -> i') -> Eff es i'
src/Cleff/Internal/Base.hs view
@@ -17,17 +17,21 @@ module Cleff.Internal.Base   ( -- * The 'IOE' Effect     IOE-  , -- * Primitive 'IO' functions-    primLiftIO, primUnliftIO-  , -- * Unwrapping 'Eff'-    thisIsPureTrustMe, runIOE, runPure-  , -- * Effect interpretation-    HandlerIO, interpretIO-  , -- * Combinators for interpreting higher-order effects-    withToIO, fromIO+    -- * Primitive 'IO' functions+  , primLiftIO+  , primUnliftIO+    -- * Unwrapping 'Eff'+  , thisIsPureTrustMe+  , runIOE+  , runPure+    -- * Effect interpretation+  , HandlerIO+  , interpretIO+    -- * Combinators for interpreting higher-order effects+  , withToIO+  , fromIO   ) where -import           Cleff.Internal.Effect import           Cleff.Internal.Interpret import           Cleff.Internal.Monad import           Control.Monad.Base          (MonadBase (liftBase))@@ -43,22 +47,24 @@  -- * The 'IOE' effect --- | The effect for lifting and unlifting the 'IO' monad, allowing you to use 'MonadIO', 'MonadUnliftIO', 'PrimMonad',--- 'MonadCatch', 'MonadThrow' and 'MonadMask' functionalities. This is the "final" effect that most effects eventually--- are interpreted into. For example, you can do:+-- | The effect capable of lifting and unlifting the 'IO' monad, allowing you to use 'MonadIO', 'MonadUnliftIO',+-- 'PrimMonad', 'MonadCatch', 'MonadThrow' and 'MonadMask' functionalities. This is the "final" effect that most+-- effects eventually are interpreted into. For example, you can do: -- -- @ -- log :: 'IOE' :> es => 'Eff' es () -- log = 'liftIO' ('putStrLn' "Test logging") -- @ ----- It is not recommended to use this effect in application code, as it is too liberal and allows arbitrary IO. Ideally,--- this is only used in interpreting more fine-grained effects.+-- It is not recommended to use this effect directly in application code, as it is too liberal and allows arbitrary IO,+-- therefore making it harder to do proper effect management. Ideally, this is only used in interpreting more+-- fine-grained effects. --+-- === Technical details+-- -- Note that this is /not/ a real effect and cannot be interpreted in any way besides 'thisIsPureTrustMe' and--- 'runIOE'. It is similar to Polysemy's @Final@ effect which also cannot be interpreted. This is mainly for--- performance concern, but also that there doesn't really exist reasonable interpretations other than the current one,--- given the underlying implementation of the 'Eff' monad.+-- 'runIOE'. This is mainly for performance concern, but also that there doesn't really exist reasonable+-- interpretations other than the current one, given the underlying implementation of the 'Eff' monad. -- -- 'IOE' can be a real effect though, and you can enable the @dynamic-ioe@ build flag to have that. However it is only -- for reference purposes and should not be used in production code.
− src/Cleff/Internal/Effect.hs
@@ -1,33 +0,0 @@-{-# OPTIONS_HADDOCK not-home #-}--- |--- Copyright: (c) 2021 Xy Ren--- License: BSD3--- Maintainer: xy.r@outlook.com--- Stability: unstable--- Portability: non-portable (GHC only)------ This module contains definitions of some basic types related to effects. You won't need this module directly;--- these functionalities are reexported in the "Cleff" module.------ __This is an /internal/ module and its API may change even between minor versions.__ Therefore you should be--- extra careful if you're to depend on this module.-module Cleff.Internal.Effect (Effect, (:>), (:>>), type (++), type (~>)) where--import           Data.Kind           (Constraint, Type)-import           Data.Rec.SmallArray (Elem, type (++), type (~>))---- | The type of effects. An effect @e m a@ takes an effect monad type @m :: 'Type' -> 'Type'@ and a result type--- @a :: 'Type'@.-type Effect = (Type -> Type) -> Type -> Type---- | @e ':>' es@ means the effect @e@ is present in the effect stack @es@, and therefore can be used in an--- @'Cleff.Eff' es@ computation.-type (:>) = Elem-infix 0 :>---- | @xs ':>>' es@ means the list of effects @xs@ are all present in the effect stack @es@. This is a convenient type--- alias for @(e1 ':>' es, ..., en ':>' es)@.-type family xs :>> es :: Constraint where-  '[] :>> _ = ()-  (x ': xs) :>> es = (x :> es, xs :>> es)-infix 0 :>>
src/Cleff/Internal/Instances.hs view
@@ -13,15 +13,14 @@ -- extra careful if you're to depend on this module. module Cleff.Internal.Instances () where -import           Cleff.Internal.Monad (Eff)+import           Cleff.Internal.Monad (Eff (Eff)) import           Control.Applicative  (Applicative (liftA2)) import           Control.Monad.Zip    (MonadZip (munzip, mzipWith))+import           Data.Monoid          (Ap (Ap)) import           Data.String          (IsString (fromString))  -- | @since 0.2.1.0-instance Bounded a => Bounded (Eff es a) where-  minBound = pure minBound-  maxBound = pure maxBound+deriving via (Ap (Eff es) a) instance Bounded a => Bounded (Eff es a)  -- | @since 0.2.1.0 instance Num a => Num (Eff es a) where@@ -61,12 +60,10 @@   atanh = fmap atanh  -- | @since 0.2.1.0-instance Semigroup a => Semigroup (Eff es a) where-  (<>) = liftA2 (<>)+deriving newtype instance Semigroup a => Semigroup (Eff es a)  -- | @since 0.2.1.0-instance Monoid a => Monoid (Eff es a) where-  mempty = pure mempty+deriving newtype instance Monoid a => Monoid (Eff es a)  -- | @since 0.2.1.0 instance IsString a => IsString (Eff es a) where
src/Cleff/Internal/Interpret.hs view
@@ -15,22 +15,43 @@ -- extra careful if you're to depend on this module. module Cleff.Internal.Interpret   ( -- * Trivial handling-    adjust, raise, raiseN, inject, subsume, subsumeN, raiseUnder, raiseNUnder, raiseUnderN, raiseNUnderN-  , -- * Handler types-    Handling, esSend, Handler, Translator-  , -- * Interpreting effects-    interpret, reinterpret, reinterpret2, reinterpret3, reinterpretN, interpose, impose, imposeN-  , -- * Translating effects-    transform, translate-  , -- * Combinators for interpreting higher effects-    toEff, toEffWith, withFromEff+    adjust+  , raise+  , raiseN+  , inject+  , subsume+  , subsumeN+  , raiseUnder+  , raiseNUnder+  , raiseUnderN+  , raiseNUnderN+    -- * Handler types+  , Handling+  , esSend+  , Handler+  , Translator+    -- * Interpreting effects+  , interpret+  , reinterpret+  , reinterpret2+  , reinterpret3+  , reinterpretN+  , interpose+  , impose+  , imposeN+    -- * Translating effects+  , transform+  , translate+    -- * Combinators for interpreting higher effects+  , toEff+  , toEffWith+  , withFromEff   ) where -import           Cleff.Internal.Effect import           Cleff.Internal.Monad-import           Data.Rec.SmallArray   (Rec, pattern (:++:))-import qualified Data.Rec.SmallArray   as Rec-import           Unsafe.Coerce         (unsafeCoerce)+import           Data.Rec.SmallArray  (Rec, pattern (:++:))+import qualified Data.Rec.SmallArray  as Rec+import           Unsafe.Coerce        (unsafeCoerce)  -- * Trivial handling 
src/Cleff/Internal/Monad.hs view
@@ -13,23 +13,60 @@ -- __This is an /internal/ module and its API may change even between minor versions.__ Therefore you should be -- extra careful if you're to depend on this module. module Cleff.Internal.Monad-  ( -- * The 'Eff' monad-    InternalHandler (InternalHandler, runHandler), Eff (Eff, unEff)-  , -- * Effect environment-    Env, HandlerPtr, emptyEnv, adjustEnv, allocaEnv, readEnv, writeEnv, replaceEnv, appendEnv, updateEnv-  , -- * Performing effect operations-    KnownList, Subset, send, sendVia+  ( -- * Basic types+    Effect+  , type (:>)+  , type (:>>)+  , type (~>)+  , type (++)+    -- * The 'Eff' monad+  , InternalHandler (InternalHandler, runHandler)+  , Eff (Eff, unEff)+    -- * Effect environment+  , Env+  , HandlerPtr+  , emptyEnv+  , adjustEnv+  , allocaEnv+  , readEnv+  , writeEnv+  , replaceEnv+  , appendEnv+  , updateEnv+    -- * Performing effect operations+  , KnownList+  , Subset+  , send+  , sendVia   ) where  import           Cleff.Internal.Any-import           Cleff.Internal.Effect-import           Control.Applicative   (Applicative (liftA2))-import           Control.Monad.Fix     (MonadFix (mfix))-import           Data.IntMap.Strict    (IntMap)-import qualified Data.IntMap.Strict    as Map-import           Data.Rec.SmallArray   (KnownList, Rec, Subset, pattern (:~:))-import qualified Data.Rec.SmallArray   as Rec+import           Control.Applicative (Applicative (liftA2))+import           Control.Monad.Fix   (MonadFix (mfix))+import           Data.IntMap.Strict  (IntMap)+import qualified Data.IntMap.Strict  as Map+import           Data.Kind           (Constraint, Type)+import           Data.Rec.SmallArray (Elem, KnownList, Rec, Subset, pattern (:~:), type (++), type (~>))+import qualified Data.Rec.SmallArray as Rec +-- * Basic types++-- | The type of effects. An effect @e m a@ takes an effect monad type @m :: 'Type' -> 'Type'@ and a result type+-- @a :: 'Type'@.+type Effect = (Type -> Type) -> Type -> Type++-- | @e ':>' es@ means the effect @e@ is present in the effect stack @es@, and therefore can be used in an+-- @'Cleff.Eff' es@ computation.+type (:>) = Elem+infix 0 :>++-- | @xs ':>>' es@ means the list of effects @xs@ are all present in the effect stack @es@. This is a convenient type+-- alias for @(e1 ':>' es, ..., en ':>' es)@.+type family xs :>> es :: Constraint where+  '[] :>> _ = ()+  (x ': xs) :>> es = (x :> es, xs :>> es)+infix 0 :>>+ -- * The 'Eff' monad  -- | The internal representation of effect handlers. This is just a natural transformation from the effect type@@ -37,19 +74,18 @@ -- -- In interpreting functions (see "Cleff.Internal.Interpret"), the user-facing 'Cleff.Handler' type is transformed into -- this type.-newtype InternalHandler e = InternalHandler-  { runHandler :: ∀ es. e (Eff es) ~> Eff es }+newtype InternalHandler e = InternalHandler { runHandler :: ∀ es. e (Eff es) ~> Eff es } --- | The extensible effect monad. A monad @'Eff' es@ is capable of performing any effect in the /effect stack/ @es@,+-- | The extensible effects monad. The monad @'Eff' es@ is capable of performing any effect in the /effect stack/ @es@, -- which is a type-level list that holds all effects available. However, most of the times, for flexibility, @es@ -- should be a polymorphic type variable, and you should use the '(:>)' and '(:>>)' operators in constraints to -- indicate what effects are in the stack. For example, -- -- @--- 'Cleff.Reader.Reader' 'String' ':>' es, 'Cleff.State.State' 'Bool' ':>' es => 'Eff' es 'Integer'+-- ('Cleff.Reader.Reader' 'String' ':>' es, 'Cleff.State.State' 'Bool' ':>' es) => 'Eff' es 'Integer' -- @ ----- allows you to perform operations of the @'Cleff.Reader.Reader' 'String'@ effect and the @'Cleff.State.State' 'Bool'@+-- means you can perform operations of the @'Cleff.Reader.Reader' 'String'@ effect and the @'Cleff.State.State' 'Bool'@ -- effect in a computation returning an 'Integer'. type role Eff nominal representational newtype Eff es a = Eff { unEff :: Env es -> IO a }@@ -75,9 +111,10 @@   {-# INLINE liftA2 #-}  instance Monad (Eff es) where+  -- no 'return', because the default impl is correct and it is going to be deprecated anyway   Eff x >>= f = Eff \es -> x es >>= \x' -> unEff (f x') es   {-# INLINE (>>=) #-}-  (>>) = (*>)+  (>>) = (*>) -- More efficient, since the default is @x >> y = x >>= const y@   {-# INLINE (>>) #-}  instance MonadFix (Eff es) where@@ -92,10 +129,7 @@ -- effect interpretation ('Cleff.reinterpretN') and the latter for local interpretation ('Cleff.toEffWith') in order to -- retain correct HO semantics. For more details on this see https://github.com/re-xyr/cleff/issues/5. type role Env nominal-data Env (es :: [Effect]) = Env-  {-# UNPACK #-} !(Rec HandlerPtr es) -- ^ The array.-  {-# UNPACK #-} !Int -- ^ The next memory address to allocate.-  !(IntMap Any) -- ^ The simulated memory.+data Env (es :: [Effect]) = Env {-# UNPACK #-} !Int {-# UNPACK #-} !(Rec HandlerPtr es) !(IntMap Any)  -- | A pointer to 'InternalHandler' in an 'Env'. type role HandlerPtr nominal@@ -103,42 +137,42 @@  -- | Create an empty 'Env' with no address allocated. emptyEnv :: Env '[]-emptyEnv = Env Rec.empty 0 Map.empty+emptyEnv = Env 0 Rec.empty Map.empty {-# INLINE emptyEnv #-}  -- | Adjust the effect stack via an function over 'Rec'. adjustEnv :: ∀ es' es. (Rec HandlerPtr es -> Rec HandlerPtr es') -> Env es -> Env es'-adjustEnv f (Env re n mem) = Env (f re) n mem+adjustEnv f (Env n re mem) = Env n (f re) mem {-# INLINE adjustEnv #-}  -- | Allocate a new, empty address for a handler. \( O(1) \). allocaEnv :: ∀ e es. Env es -> (# HandlerPtr e, Env es #)-allocaEnv (Env re n mem) = (# HandlerPtr n, Env re (succ n) mem #)+allocaEnv (Env n re mem) = (# HandlerPtr n, Env (n + 1) re mem #) {-# INLINE allocaEnv #-}  -- | Read the handler a pointer points to. \( O(1) \). readEnv :: ∀ e es. Rec.Elem e es => Env es -> InternalHandler e-readEnv (Env re _ mem) = fromAny $ mem Map.! unHandlerPtr (Rec.index @e re)+readEnv (Env _ re mem) = fromAny $ mem Map.! unHandlerPtr (Rec.index @e re) {-# INLINE readEnv #-}  -- | Overwrite the handler a pointer points to. \( O(1) \). writeEnv :: ∀ e es. HandlerPtr e -> InternalHandler e -> Env es -> Env es-writeEnv (HandlerPtr m) x (Env re n mem) = Env re n (Map.insert m (toAny x) mem)+writeEnv (HandlerPtr m) x (Env n re mem) = Env n re (Map.insert m (toAny x) mem) {-# INLINE writeEnv #-}  -- | Replace the handler pointer of an effect in the stack. \( O(n) \). replaceEnv :: ∀ e es. Rec.Elem e es => HandlerPtr e -> InternalHandler e -> Env es -> Env es-replaceEnv (HandlerPtr m) x (Env re n mem) = Env (Rec.update @e (HandlerPtr m) re) n (Map.insert m (toAny x) mem)+replaceEnv (HandlerPtr m) x (Env n re mem) = Env n (Rec.update @e (HandlerPtr m) re) (Map.insert m (toAny x) mem) {-# INLINE replaceEnv #-}  -- | Add a new effect to the stack with its corresponding handler pointer. \( O(n) \). appendEnv :: ∀ e es. HandlerPtr e -> InternalHandler e -> Env es -> Env (e ': es)-appendEnv (HandlerPtr m) x (Env re n mem) = Env (HandlerPtr m :~: re) n (Map.insert m (toAny x) mem)+appendEnv (HandlerPtr m) x (Env n re mem) = Env n (HandlerPtr m :~: re) (Map.insert m (toAny x) mem) {-# INLINE appendEnv #-}  -- | Use the state of LHS as a newer version for RHS. \( O(1) \). updateEnv :: ∀ es es'. Env es' -> Env es -> Env es-updateEnv (Env _ n mem) (Env re' _ _) = Env re' n mem+updateEnv (Env n _ mem) (Env _ re' _) = Env n re' mem {-# INLINE updateEnv #-}  -- * Performing effect operations@@ -149,8 +183,8 @@ send = sendVia id  -- | Perform an action in another effect stack via a transformation to that stack; in other words, this function "maps"--- the effect operation from effect stack @es@ to @es'@. This is a generalization of 'send'; end users most likely--- won't need to use this.+-- the effect operation from effect stack @es@ to @es'@. This is a largely generalized version of 'send'; only use this+-- if you are sure about what you're doing. -- -- @ -- 'send' = 'sendVia' 'id'
src/Cleff/Internal/TH.hs view
@@ -15,7 +15,6 @@ -- extra careful if you're to depend on this module. module Cleff.Internal.TH (makeEffect, makeEffect_) where -import           Cleff.Internal.Effect import           Cleff.Internal.Monad import           Control.Monad                (join) import           Data.Char                    (toLower)
src/Cleff/Mask.hs view
@@ -8,30 +8,97 @@ module Cleff.Mask   ( -- * Effect     Mask (..)-  , -- * Operations-    mask, uninterruptibleMask, bracket, bracketOnError, mask_, uninterruptibleMask_, bracket_, finally, onError-  , -- * Interpretations-    runMask+    -- * High-level operations+  , bracket+  , bracketOnError+  , bracket_+  , bracketOnError_+  , onError+  , finally+    -- * Primitive operations+  , mask+  , uninterruptibleMask+  , onException+  , mask_+  , uninterruptibleMask_+    -- * Interpretations+  , runMask   ) where  import           Cleff import           Cleff.Internal.Base-import qualified UnliftIO.Exception  as Exc+import qualified Control.Exception   as Exc  -- * Effect --- | An effect capable of 'Exc.mask'ing and specifically, 'Exc.bracket'ing operations, /i.e./ allowing cleanup after--- operations that my raise exceptions.+-- | An effect capable of 'Exc.mask'ing and performing cleanup operations when an computation is interrupted. In+-- particular, this effects allows the use of 'bracket'.+--+-- === Technical details+--+-- Regarding the nuances of 'bracket' semantics, this effect uses the semantics of "UnliftIO.Exception" rather than+-- "Control.Exception". They are more sensible defaults and users can implement other semantics out of the primitive+-- operations if they want to. data Mask :: Effect where   Mask :: ((m ~> m) -> m a) -> Mask m a   UninterruptibleMask :: ((m ~> m) -> m a) -> Mask m a-  Bracket :: m a -> (a -> m c) -> (a -> m b) -> Mask m b-  BracketOnError :: m a -> (a -> m c) -> (a -> m b) -> Mask m b+  OnException :: m a -> m b -> Mask m a  -- * Operations -makeEffect ''Mask+makeEffect_ ''Mask +-- | Prevents a computation from receiving asynchronous exceptions, i.e. being interrupted by another thread. Also+-- provides a function to restore receiving async exceptions for a computation.+--+-- However, some potentially blocking actions like @takeMVar@ can still be interrupted, and for them also not to be+-- interrupted in any case you'll need 'uninterruptibleMask'. See 'Control.Exception.mask' for details.+mask :: Mask :> es => ((Eff es ~> Eff es) -> Eff es a) -> Eff es a++-- | Prevents a computation from receiving asynchronous exceptions, even if there is an interruptible operation+-- (operations that potentially deadlocks or otherwise blocks indefinitely). Therefore this function is potentially+-- dangerous in the sense that it can make a thread both unresponsive and unkillable. See+-- 'Control.Exception.uninterruptibleMask' for details.+uninterruptibleMask :: Mask :> es => ((Eff es ~> Eff es) -> Eff es a) -> Eff es a++-- | Like 'onError', but without 'uninterruptibleMask'ing the cleanup action, making it possible that a cleanup action+-- is interrupted. Use 'onError' is usually the safer option.+onException :: Mask :> es+  => Eff es a -- ^ The main computation that may throw an exception+  -> Eff es b -- ^ The computation that runs when an exception is thrown+  -> Eff es a++-- | Run a computation that acquires a resource (@alloc@), then a main computation using that resource, then a cleanup+-- computation (@dealloc@). 'bracket' guarantees that @alloc@ and @dealloc@ will always run, regardless of whether an+-- exception is thrown in the main computation. Note that if an exception is thrown in the main computation, it will+-- be rethrown after 'bracket' finishes.+--+-- === Technical details+--+-- Note that this function uses @unliftio@ semantics: resource acquiring action is interruptibly 'mask'ed while+-- resource cleanup is 'uninterruptibleMask'ed. Most of the times, this will be what you want. Other functions in this+-- module use @unliftio@ semantics too.+bracket :: Mask :> es+  => Eff es a -- ^ The computation to run first, usually acquires a resource+  -> (a -> Eff es c) -- ^ The computation to run after the main computation, usually cleans up+  -> (a -> Eff es b) -- ^ The main computation that uses the resource+  -> Eff es b+bracket alloc dealloc action = mask \restore -> do+  res <- alloc+  ret <- restore (action res) `onError` dealloc res+  _ <- uninterruptibleMask_ (dealloc res)+  pure ret++-- | Like 'bracket', but only runs cleanup if an exception is thrown in the main computation.+bracketOnError :: Mask :> es+  => Eff es a -- ^ The computation to run first, usually acquires a resource+  -> (a -> Eff es c) -- ^ The computation to run when the main computation throws an exception, usually cleans up+  -> (a -> Eff es b) -- ^ The main computation that uses the resource+  -> Eff es b+bracketOnError alloc dealloc action = mask \restore -> do+  res <- alloc+  restore (action res) `onError` dealloc res+ -- | Variant of 'mask' that does not provide a restoring function. mask_ :: Mask :> es => Eff es a -> Eff es a mask_ m = mask \_ -> m@@ -44,21 +111,34 @@ bracket_ :: Mask :> es => Eff es a -> Eff es c -> (a -> Eff es b) -> Eff es b bracket_ ma = bracket ma . const --- | Attach a cleanup action that will always run to a potentially throwing computation.-finally :: Mask :> es => Eff es a -> Eff es b -> Eff es a-finally m mz = bracket_ (pure ()) mz (const m)+-- | Variant of 'bracketOnError' that does not pass the allocated resource to the cleanup action.+bracketOnError_ :: Mask :> es => Eff es a -> Eff es c -> (a -> Eff es b) -> Eff es b+bracketOnError_ ma = bracketOnError ma . const --- | Attach an action that runs if the main computation throws an exception.-onError :: Mask :> es => Eff es a -> Eff es b -> Eff es a-onError m mz = bracketOnError (pure ()) (const mz) (const m)+-- | Attach an action that runs if the main computation throws an exception. Note that this will rethrow the exception+-- instead of returning to normal control flow.+--+-- The cleanup action is guaranteed not to be interrupted halfways.+onError :: Mask :> es+  => Eff es a -- ^ The main computation that may throw an exception+  -> Eff es b -- ^ The computation that runs when an exception is thrown+  -> Eff es a+onError m n = m `onException` uninterruptibleMask_ n +-- | Attach a cleanup action that will always run after a potentially throwing computation.+finally :: Mask :> es+  => Eff es a -- ^ The main computation that may throw an exception+  -> Eff es b -- ^ The computation that runs after the main computation, regardless of whether an exception is thrown+  -> Eff es a+finally m mz = (m `onError` mz) <* uninterruptibleMask_ mz+ -- * Interpretations  -- | Interpret the 'Mask' effect in terms of primitive 'IO' actions. runMask :: Eff (Mask ': es) ~> Eff es runMask = thisIsPureTrustMe . reinterpret \case-  Mask f                 -> withToIO \toIO -> Exc.mask \restore -> toIO $ f (fromIO . restore . toIO)-  UninterruptibleMask f  -> withToIO \toIO -> Exc.uninterruptibleMask \restore -> toIO $ f (fromIO . restore . toIO)-  Bracket ma mz m        -> withToIO \toIO -> Exc.bracket (toIO ma) (toIO . mz) (toIO . m)-  BracketOnError ma mz m -> withToIO \toIO -> Exc.bracketOnError (toIO ma) (toIO . mz) (toIO . m)+  Mask f -> withToIO \toIO -> Exc.mask \restore -> toIO $ f (fromIO . restore . toIO)+  UninterruptibleMask f -> withToIO \toIO -> Exc.uninterruptibleMask \restore -> toIO $ f (fromIO . restore . toIO)+  OnException m n -> withToIO \toIO -> toIO m `Exc.catch` \(e :: Exc.SomeException) ->+    Exc.try @Exc.SomeException (toIO n) *> Exc.throwIO e {-# INLINE runMask #-}
src/Cleff/Output.hs view
@@ -8,10 +8,15 @@ module Cleff.Output   ( -- * Effect     Output (..)-  , -- * Operations-    output-  , -- * Interpretations-    outputToListState, outputToWriter, ignoreOutput, runOutputEff, mapOutput, bindOutput+    -- * Operations+  , output+    -- * Interpretations+  , outputToListState+  , outputToWriter+  , ignoreOutput+  , runOutputEff+  , mapOutput+  , bindOutput   ) where  import           Cleff@@ -20,14 +25,17 @@  -- * Effect --- | An effect that is capable of sending outputs, for example to a log file or an output stream.+-- | An effect that is capable of producing outputs, for example writing to a log file or an output stream. data Output o :: Effect where   Output :: o -> Output o m ()  -- * Operations -makeEffect ''Output+makeEffect_ ''Output +-- | Produce an output value.+output :: Output o :> es => o -> Eff es ()+ -- * Interpretations  -- | Run an 'Output' effect by accumulating a list. Note that outputs are being prepended to the head of the list, so@@ -63,7 +71,7 @@   Output x -> output $ f x {-# INLINE mapOutput #-} --- | Transform an 'Input' effect into another one already in the effect stack, by an effectful computation.+-- | Transform an 'Output' effect into another one already in the effect stack, by an effectful computation. -- -- @since 0.2.1.0 bindOutput :: Output o' :> es => (o -> Eff es o') -> Eff (Output o ': es) ~> Eff es
src/Cleff/Reader.hs view
@@ -8,10 +8,13 @@ module Cleff.Reader   ( -- * Effect     Reader (..)-  , -- * Operations-    ask, local, asks-  , -- * Interpretations-    runReader, magnify+    -- * Operations+  , ask+  , local+  , asks+    -- * Interpretations+  , runReader+  , magnify   ) where  import           Cleff@@ -20,16 +23,25 @@ -- * Effect  -- | An effect capable of providing an immutable environment @r@ that can be read. This roughly corresponds to the--- @MonadReader@ typeclass and @ReaderT@ monad transformer in the @mtl@ approach.+-- @MonadReader@ typeclass and @ReaderT@ monad transformer in the @mtl@ library. data Reader r :: Effect where   Ask :: Reader r m r   Local :: (r -> r) -> m a -> Reader r m a  -- * Operations -makeEffect ''Reader+makeEffect_ ''Reader --- | Apply a function on the result of 'ask'.+-- | Obtain the environment value.+ask :: Reader r :> es => Eff es r++-- | Modify the environment value temporarily for a computation.+local :: Reader r :> es+  => (r -> r) -- ^ The function that modifies the environment+  -> Eff es a -- ^ The computation to run with the modified environment+  -> Eff es a++-- | Apply a function to the result of 'ask'. asks :: Reader r :> es => (r -> s) -> Eff es s asks = (<$> ask) 
src/Cleff/State.hs view
@@ -8,10 +8,18 @@ module Cleff.State   ( -- * Effect     State (..)-  , -- * Operations-    get, put, state, gets, modify-  , -- * Interpretations-    runState, runStateIORef, runStateMVar, runStateTVar, zoom+    -- * Operations+  , get+  , put+  , state+  , gets+  , modify+    -- * Interpretations+  , runState+  , runStateIORef+  , runStateMVar+  , runStateTVar+  , zoom   ) where  import           Cleff@@ -27,7 +35,7 @@ -- * Effect  -- | An effect capable of providing a mutable state @s@ that can be read and written. This roughly corresponds to the--- @MonadState@ typeclass and @StateT@ monad transformer in the @mtl@ approach.+-- @MonadState@ typeclass and @StateT@ monad transformer in the @mtl@ library. data State s :: Effect where   Get :: State s m s   Put :: s -> State s m ()@@ -35,8 +43,19 @@  -- * Operations -makeEffect ''State+makeEffect_ ''State +-- | Read the current state.+get :: State s :> es => Eff es s++-- | Update the state with a new value.+put :: State s :> es => s -> Eff es ()++-- | Modify the state /and/ produce a value from the state via a function.+state :: State s :> es+  => (s -> (a, s)) -- ^ The function that takes the state and returns a result value together with a modified state+  -> Eff es a+ -- | Apply a function to the result of 'get'. gets :: State s :> es => (s -> t) -> Eff es t gets = (<$> get)@@ -56,7 +75,9 @@  -- | Run the 'State' effect. ----- __Caveat__: The 'runState' interpreter is implemented with 'Data.IORef.IORef's and there is no way to do arbitrary+-- === Caveats+--+-- The 'runState' interpreter is implemented with 'Data.IORef.IORef's and there is no way to do arbitrary -- atomic transactions. The 'state' operation is atomic though and it is implemented with 'atomicModifyIORefCAS', which -- can be faster than @atomicModifyIORef@ in contention. For any more complicated cases of atomicity, please build your -- own effect that uses either @MVar@s or @TVar@s based on your need.
src/Cleff/Trace.hs view
@@ -8,10 +8,14 @@ module Cleff.Trace   ( -- * Effect     Trace (..)-  , -- * Operations-    trace-  , -- * Interpretations-    runTraceHandle, runTraceStdout, runTraceStderr, ignoreTrace, traceToOutput+    -- * Operations+  , trace+    -- * Interpretations+  , runTraceHandle+  , runTraceStdout+  , runTraceStderr+  , ignoreTrace+  , traceToOutput   ) where  import           Cleff@@ -26,7 +30,10 @@  -- * Operations -makeEffect ''Trace+makeEffect_ ''Trace++-- | Output a trace message.+trace :: Trace :> es => String -> Eff es ()  -- * Interpretations 
src/Cleff/Writer.hs view
@@ -8,10 +8,13 @@ module Cleff.Writer   ( -- * Effect     Writer (..)-  , -- * Operations-    tell, listen, listens-  , -- * Interpretations-    runWriter, runWriterBatch+    -- * Operations+  , tell+  , listen+  , listens+    -- * Interpretations+  , runWriter+  , runWriterBatch   ) where  import           Cleff@@ -22,8 +25,8 @@  -- * Effect --- | An effect capable of accumulating outputs. This roughly corresponds to the @MonadWriter@ typeclass and @WriterT@--- monad transformer in the @mtl@ approach.+-- | An effect capable of accumulating monoidal outputs. This roughly corresponds to the @MonadWriter@ typeclass and+-- @WriterT@ monad transformer in the @mtl@ library. -- -- However, note that this does not have a @pass@ operation as we are not sure what its semantics should be. In fact, -- the @pass@ semantics in @mtl@ is also unclear and will change when handlers are put in different orders. To avoid@@ -34,8 +37,14 @@  -- * Operations -makeEffect ''Writer+makeEffect_ ''Writer +-- | Produces an output that is appended to the accumulated value.+tell :: Writer w :> es => w -> Eff es ()++-- | Monitor the output of a computation, and return the output alongside the computation's result.+listen :: Writer w :> es => Eff es a -> Eff es (a, w)+ -- | Apply a function to the accumulated output of 'listen'. listens :: Writer w :> es => (w -> x) -> Eff es a -> Eff es (a, x) listens f m = do@@ -46,7 +55,9 @@  -- | Run a monoidal 'Writer' effect. ----- __Caveat__: Both 'runWriter' and 'listen's under 'runWriter' will stop taking care of writer operations done on+-- === Caveats+--+-- Both 'runWriter' and 'listen's under 'runWriter' will stop taking care of writer operations done on -- forked threads as soon as the main thread finishes its computation. Any writer operation done -- /before main thread finishes/ is still taken into account. runWriter :: ∀ w es a. Monoid w => Eff (Writer w ': es) a -> Eff es (a, w)