-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | An easy to use, performant extensible effects library. -- -- An easy to use, performant extensible effects library with seamless -- integration with the existing Haskell ecosystem. -- -- This library provides core definitions with a minimal dependency -- footprint. See the effectful package for the -- "batteries-included" variant. @package effectful-core @version 2.2.2.2 -- | Type-safe indexing for Env. -- -- This module is intended for internal use only, and may change without -- warning in subsequent releases. module Effectful.Internal.Effect -- | The kind of effects. type Effect = (Type -> Type) -> Type -> Type -- | A constraint that requires that a particular effect e is a -- member of the type-level list es. This is used to -- parameterize an Eff computation over an arbitrary list of -- effects, so long as e is somewhere in the list. -- -- For example, a computation that only needs access to a mutable value -- of type Integer would have the following type: -- --
-- State Integer :> es => Eff es () --class (e :: Effect) :> (es :: [Effect]) -- | Get the position of e in es. -- -- Note: GHC is kind enough to cache these values as they're top -- level CAFs, so the lookup is amortized O(1) without any -- language level tricks. reifyIndex :: (:>) e es => Int -- | Convenience operator for expressing that a function uses multiple -- effects in a more concise way than enumerating them all with -- (:>). -- --
-- [E1, E2, ..., En] :>> es ≡ (E1 :> es, E2 :> es, ..., En :> es) ---- | Deprecated: Usage of (:>>) slows down GHC too much, so it -- will be removed in 3.0.0.0. See -- https://github.com/haskell-effectful/effectful/issues/52#issuecomment-1269155485 -- for more information. type family xs :>> es :: Constraint -- | Provide evidence that xs is a subset of es. class KnownPrefix es => Subset (xs :: [Effect]) (es :: [Effect]) subsetFullyKnown :: Subset xs es => Bool reifyIndices :: Subset xs es => [Int] -- | Calculate length of a statically known prefix of es. class KnownPrefix (es :: [Effect]) prefixLength :: KnownPrefix es => Int -- | Require that xs is the unknown suffix of es. class (xs :: [Effect]) `IsUnknownSuffixOf` (es :: [Effect]) -- | The kind of types with lifted values. For example Int :: -- Type. type Type = TYPE LiftedRep instance (Effectful.Internal.Effect.KnownPrefix es, Effectful.Internal.Effect.IsUnknownSuffixOf xs es) => Effectful.Internal.Effect.Subset xs es instance (xs GHC.Types.~ es) => Effectful.Internal.Effect.IsUnknownSuffixOf xs es instance Effectful.Internal.Effect.IsUnknownSuffixOf xs es => Effectful.Internal.Effect.IsUnknownSuffixOf xs (e : es) instance Effectful.Internal.Effect.KnownPrefix es => Effectful.Internal.Effect.Subset '[] es instance (e Effectful.Internal.Effect.:> es, Effectful.Internal.Effect.Subset xs es) => Effectful.Internal.Effect.Subset (e : xs) es instance Effectful.Internal.Effect.KnownPrefix es => Effectful.Internal.Effect.KnownPrefix (e : es) instance Effectful.Internal.Effect.KnownPrefix es instance (TypeError ...) => e Effectful.Internal.Effect.:> '[] instance e Effectful.Internal.Effect.:> (e : es) instance (e Effectful.Internal.Effect.:> es) => e Effectful.Internal.Effect.:> (x : es) module Effectful.Internal.Utils -- | Version of bracket with an INLINE pragma to work around -- https://gitlab.haskell.org/ghc/ghc/-/issues/22824. inlineBracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c -- | Get an id of a thread that doesn't prevent its garbage collection. weakThreadId :: ThreadId -> Int eqThreadId :: ThreadId -> ThreadId -> Bool -- | The type constructor Any is type to which you can unsafely -- coerce any lifted type, and back. More concretely, for a lifted type -- t and value x :: t, -- unsafeCoerce -- (unsafeCoerce x :: Any) :: t is equivalent to x. type family Any :: k toAny :: a -> Any fromAny :: Any -> a -- | A strict variant of IORef. data IORef' a newIORef' :: a -> IO (IORef' a) readIORef' :: IORef' a -> IO a writeIORef' :: IORef' a -> a -> IO () -- | A strict variant of MVar. data MVar' a toMVar' :: MVar a -> IO (MVar' a) newMVar' :: a -> IO (MVar' a) readMVar' :: MVar' a -> IO a modifyMVar' :: MVar' a -> (a -> IO (a, r)) -> IO r modifyMVar_' :: MVar' a -> (a -> IO a) -> IO () instance GHC.Classes.Eq (Effectful.Internal.Utils.IORef' a) instance GHC.Classes.Eq (Effectful.Internal.Utils.MVar' a) module Effectful.Internal.Env -- | A strict (WHNF), thread local, mutable, extensible record -- indexed by types of kind Effect. -- -- Warning: the environment is a mutable data structure and cannot be -- simultaneously used from multiple threads under any circumstances. -- -- In order to pass it to a different thread, you need to perform a deep -- copy with the cloneEnv funtion. -- -- Offers very good performance characteristics for most often performed -- operations: -- --
-- (Reader String :> es, State Bool :> es) => Eff es Integer ---- -- Abstracting over the list of effects with (:>): -- --
-- >>> data E1 :: Effect -- -- >>> data E2 :: Effect -- -- >>> data E3 :: Effect ---- -- It makes it possible to rearrange the effect stack however you like: -- --
-- >>> :{
-- shuffle :: Eff (E3 : E1 : E2 : es) a -> Eff (E1 : E2 : E3 : es) a
-- shuffle = inject
-- :}
--
--
-- It can also turn a monomorphic effect stack into a polymorphic one:
--
--
-- >>> :{
-- toPoly :: (E1 :> es, E2 :> es, E3 :> es) => Eff [E1, E2, E3] a -> Eff es a
-- toPoly = inject
-- :}
--
--
-- Moreover, it allows for hiding specific effects from downstream:
--
--
-- >>> :{
-- onlyE1 :: Eff (E1 : es) a -> Eff (E1 : E2 : E3 : es) a
-- onlyE1 = inject
-- :}
--
--
--
-- >>> :{
-- onlyE2 :: Eff (E2 : es) a -> Eff (E1 : E2 : E3 : es) a
-- onlyE2 = inject
-- :}
--
--
--
-- >>> :{
-- onlyE3 :: Eff (E3 : es) a -> Eff (E1 : E2 : E3 : es) a
-- onlyE3 = inject
-- :}
--
--
-- However, it's not possible to inject a computation into an
-- incompatible effect stack:
--
--
-- >>> :{
-- coerceEs :: Eff es1 a -> Eff es2 a
-- coerceEs = inject
-- :}
-- ...
-- ...Couldn't match type ‘es1’ with ‘es2’
-- ...
--
inject :: Subset xs es => Eff xs a -> Eff es a
-- | Provide evidence that xs is a subset of es.
class KnownPrefix es => Subset (xs :: [Effect]) (es :: [Effect])
-- | The strategy to use when unlifting Eff computations via
-- withEffToIO, withRunInIO or the localUnlift
-- family.
data UnliftStrategy
-- | The fastest strategy and a default setting for IOE. An attempt
-- to call the unlifting function in threads distinct from its creator
-- will result in a runtime error.
SeqUnlift :: UnliftStrategy
-- | A strategy that makes it possible for the unlifting function to be
-- called in threads distinct from its creator. See Persistence
-- and Limit settings for more information.
ConcUnlift :: !Persistence -> !Limit -> UnliftStrategy
-- | Persistence setting for the ConcUnlift strategy.
--
-- Different functions require different persistence strategies.
-- Examples:
--
-- -- IO a -> IO b ---- -- to -- --
-- Eff es a -> Eff es b ---- -- This function is highly unsafe because: -- --
-- IO a -> IO b ---- -- to -- --
-- Eff es a -> Eff es b ---- -- Note: the computation must not run its argument in a separate -- thread, attempting to do so will result in a runtime error. -- -- This function is unsafe because it can be used to introduce -- arbitrary IO actions into pure Eff computations. unsafeLiftMapIO :: HasCallStack => (IO a -> IO b) -> Eff es a -> Eff es b -- | Request a CallStack. -- -- NOTE: The implicit parameter ?callStack :: CallStack is an -- implementation detail and should not be considered part of the -- CallStack API, we may decide to change the implementation in -- the future. type HasCallStack = ?callStack :: CallStack module Effectful -- | The Eff monad provides the implementation of a computation that -- performs an arbitrary set of effects. In Eff es a, -- es is a type-level list that contains all the effects that -- the computation may perform. For example, a computation that produces -- an Integer by consuming a String from the global -- environment and acting upon a single mutable value of type Bool -- would have the following type: -- --
-- (Reader String :> es, State Bool :> es) => Eff es Integer ---- -- Abstracting over the list of effects with (:>): -- --
-- State Integer :> es => Eff es () --class (e :: Effect) :> (es :: [Effect]) -- | Convenience operator for expressing that a function uses multiple -- effects in a more concise way than enumerating them all with -- (:>). -- --
-- [E1, E2, ..., En] :>> es ≡ (E1 :> es, E2 :> es, ..., En :> es) ---- | Deprecated: Usage of (:>>) slows down GHC too much, so it -- will be removed in 3.0.0.0. See -- https://github.com/haskell-effectful/effectful/issues/52#issuecomment-1269155485 -- for more information. type family xs :>> es :: Constraint -- | Run a pure Eff computation. -- -- For running computations with side effects see runEff. runPureEff :: Eff '[] a -> a -- | Run an Eff computation with side effects. -- -- For running pure computations see runPureEff. runEff :: Eff '[IOE] a -> IO a -- | Run arbitrary IO computations via MonadIO or -- MonadUnliftIO. -- -- Note: it is not recommended to use this effect in application -- code as it is too liberal. Ideally, this is only used in handlers of -- more fine-grained effects. data IOE :: Effect -- | The strategy to use when unlifting Eff computations via -- withEffToIO, withRunInIO or the localUnlift -- family. data UnliftStrategy -- | The fastest strategy and a default setting for IOE. An attempt -- to call the unlifting function in threads distinct from its creator -- will result in a runtime error. SeqUnlift :: UnliftStrategy -- | A strategy that makes it possible for the unlifting function to be -- called in threads distinct from its creator. See Persistence -- and Limit settings for more information. ConcUnlift :: !Persistence -> !Limit -> UnliftStrategy -- | Persistence setting for the ConcUnlift strategy. -- -- Different functions require different persistence strategies. -- Examples: -- --
-- >>> data E1 :: Effect -- -- >>> data E2 :: Effect -- -- >>> data E3 :: Effect ---- -- It makes it possible to rearrange the effect stack however you like: -- --
-- >>> :{
-- shuffle :: Eff (E3 : E1 : E2 : es) a -> Eff (E1 : E2 : E3 : es) a
-- shuffle = inject
-- :}
--
--
-- It can also turn a monomorphic effect stack into a polymorphic one:
--
--
-- >>> :{
-- toPoly :: (E1 :> es, E2 :> es, E3 :> es) => Eff [E1, E2, E3] a -> Eff es a
-- toPoly = inject
-- :}
--
--
-- Moreover, it allows for hiding specific effects from downstream:
--
--
-- >>> :{
-- onlyE1 :: Eff (E1 : es) a -> Eff (E1 : E2 : E3 : es) a
-- onlyE1 = inject
-- :}
--
--
--
-- >>> :{
-- onlyE2 :: Eff (E2 : es) a -> Eff (E1 : E2 : E3 : es) a
-- onlyE2 = inject
-- :}
--
--
--
-- >>> :{
-- onlyE3 :: Eff (E3 : es) a -> Eff (E1 : E2 : E3 : es) a
-- onlyE3 = inject
-- :}
--
--
-- However, it's not possible to inject a computation into an
-- incompatible effect stack:
--
--
-- >>> :{
-- coerceEs :: Eff es1 a -> Eff es2 a
-- coerceEs = inject
-- :}
-- ...
-- ...Couldn't match type ‘es1’ with ‘es2’
-- ...
--
inject :: Subset xs es => Eff xs a -> Eff es a
-- | Provide evidence that xs is a subset of es.
class KnownPrefix es => Subset (xs :: [Effect]) (es :: [Effect])
-- | Monads in which IO computations may be embedded. Any monad
-- built by applying a sequence of monad transformers to the IO
-- monad will be an instance of this class.
--
-- Instances should satisfy the following laws, which state that
-- liftIO is a transformer of monads:
--
--
class Monad m => MonadIO (m :: Type -> Type)
-- | Lift a computation from the IO monad. This allows us to run IO
-- computations in any monadic stack, so long as it supports these kinds
-- of operations (i.e. IO is the base monad for the stack).
--
-- -- import Control.Monad.Trans.State -- from the "transformers" library -- -- printState :: Show s => StateT s IO () -- printState = do -- state <- get -- liftIO $ print state ---- -- Had we omitted liftIO, we would have ended up with -- this error: -- --
-- • Couldn't match type ‘IO’ with ‘StateT s IO’ -- Expected type: StateT s IO () -- Actual type: IO () ---- -- The important part here is the mismatch between StateT s IO -- () and IO (). -- -- Luckily, we know of a function that takes an IO a and -- returns an (m a): liftIO, enabling us to run -- the program and see the expected results: -- --
-- > evalStateT printState "hello" -- "hello" -- -- > evalStateT printState 3 -- 3 --liftIO :: MonadIO m => IO a -> m a -- | Monads which allow their actions to be run in IO. -- -- While MonadIO allows an IO action to be lifted into -- another monad, this class captures the opposite concept: allowing you -- to capture the monadic context. Note that, in order to meet the laws -- given below, the intuition is that a monad must have no monadic state, -- but may have monadic context. This essentially limits -- MonadUnliftIO to ReaderT and IdentityT -- transformers on top of IO. -- -- Laws. For any function run provided by withRunInIO, it -- must meet the monad transformer laws as reformulated for -- MonadUnliftIO: -- --
run . return = return
run (m >>= f) = run m >>= run . f
-- withRunInIO inner = -- StateT $ \s -> -- withRunInIO $ \run -> -- inner (run . flip evalStateT s) ---- -- This breaks the identity law because the inner run m would -- throw away any state changes in m. class MonadIO m => MonadUnliftIO (m :: Type -> Type) -- | Convenience function for capturing the monadic context and running an -- IO action with a runner function. The runner function is used -- to run a monadic action m in IO. withRunInIO :: MonadUnliftIO m => ((forall a. () => m a -> IO a) -> IO b) -> m b -- | Support for handling errors of a particular type, i.e. checked -- exceptions. -- -- The Error effect is not a general mechanism for handling -- regular exceptions, that's what functions from the exceptions -- library are for (see Control.Monad.Catch for more information). -- -- In particular, regular exceptions of type e are distinct from -- errors of type e and will not be caught by functions -- from this module: -- --
-- >>> import qualified Control.Monad.Catch as E ---- --
-- >>> boom = error "BOOM!" ---- --
-- >>> runEff . runError @ErrorCall $ boom `catchError` \_ (_::ErrorCall) -> pure "caught" -- *** Exception: BOOM! -- ... ---- -- If you want to catch regular exceptions, you should use catch -- (or a similar function): -- --
-- >>> runEff $ boom `E.catch` \(_::ErrorCall) -> pure "caught" -- "caught" ---- -- On the other hand, functions for safe finalization and management of -- resources such as finally and bracket work as expected: -- --
-- >>> msg = liftIO . putStrLn ---- --
-- >>> :{
-- runEff . runErrorNoCallStack @String $ do
-- E.bracket_ (msg "Beginning.")
-- (msg "Cleaning up.")
-- (msg "Computing." >> throwError "oops" >> msg "More.")
-- :}
-- Beginning.
-- Computing.
-- Cleaning up.
-- Left "oops"
--
--
-- Note: unlike the ExceptT monad transformer from the
-- transformers library, the order in which you handle the
-- Error effect with regard to other stateful effects does not
-- matter. Consider the following:
--
-- -- >>> import qualified Control.Monad.State.Strict as T -- -- >>> import qualified Control.Monad.Except as T ---- --
-- >>> m1 = (T.modify (++ " there!") >> T.throwError "oops") `T.catchError` \_ -> pure () ---- --
-- >>> (`T.runStateT` "Hi") . T.runExceptT $ m1 -- (Right (),"Hi there!") ---- --
-- >>> T.runExceptT . (`T.runStateT` "Hi") $ m1 -- Right ((),"Hi") ---- -- Here, whether state updates within the catchError block are -- discarded or not depends on the shape of the monad transformer stack, -- which is surprising and can be a source of subtle bugs. On the other -- hand: -- --
-- >>> import Effectful.State.Static.Local ---- --
-- >>> m2 = (modify (++ " there!") >> throwError "oops") `catchError` \_ (_::String) -> pure () ---- --
-- >>> runEff . runState "Hi" . runError @String $ m2 -- (Right (),"Hi there!") ---- --
-- >>> runEff . runError @String . runState "Hi" $ m2 -- Right ((),"Hi there!") ---- -- Here, no matter the order of effects, state updates made within the -- catchError block before the error happens always persist, -- giving predictable behavior. -- -- Hint: if you'd like to reproduce the transactional behavior -- with the State effect, appropriate usage of -- bracketOnError will do the trick. module Effectful.Error.Static -- | Provide the ability to handle errors of type e. data Error e :: Effect -- | Handle errors of type e. runError :: forall e es a. Eff (Error e : es) a -> Eff es (Either (CallStack, e) a) -- | Handle errors of type e. In case of an error discard the -- CallStack. runErrorNoCallStack :: forall e es a. Eff (Error e : es) a -> Eff es (Either e a) -- | Throw an error of type e. throwError :: forall e es a. (HasCallStack, Error e :> es) => e -> Eff es a -- | Handle an error of type e. catchError :: forall e es a. Error e :> es => Eff es a -> (CallStack -> e -> Eff es a) -> Eff es a -- | The same as flip catchError, which is useful in -- situations where the code for the handler is shorter. handleError :: forall e es a. Error e :> es => (CallStack -> e -> Eff es a) -> Eff es a -> Eff es a -- | Similar to catchError, but returns an Either result -- which is a Right if no error was thrown and a Left -- otherwise. tryError :: forall e es a. Error e :> es => Eff es a -> Eff es (Either (CallStack, e) a) -- | Request a CallStack. -- -- NOTE: The implicit parameter ?callStack :: CallStack is an -- implementation detail and should not be considered part of the -- CallStack API, we may decide to change the implementation in -- the future. type HasCallStack = ?callStack :: CallStack -- | CallStacks are a lightweight method of obtaining a partial -- call-stack at any point in the program. -- -- A function can request its call-site with the HasCallStack -- constraint. For example, we can define -- --
-- putStrLnWithCallStack :: HasCallStack => String -> IO () ---- -- as a variant of putStrLn that will get its call-site and -- print it, along with the string given as argument. We can access the -- call-stack inside putStrLnWithCallStack with -- callStack. -- --
-- >>> :{
-- putStrLnWithCallStack :: HasCallStack => String -> IO ()
-- putStrLnWithCallStack msg = do
-- putStrLn msg
-- putStrLn (prettyCallStack callStack)
-- :}
--
--
-- Thus, if we call putStrLnWithCallStack we will get a
-- formatted call-stack alongside our string.
--
-- -- >>> putStrLnWithCallStack "hello" -- hello -- CallStack (from HasCallStack): -- putStrLnWithCallStack, called at <interactive>:... in interactive:Ghci... ---- -- GHC solves HasCallStack constraints in three steps: -- --
-- interpret ≡ reinterpret id --reinterpret :: DispatchOf e ~ Dynamic => (Eff handlerEs a -> Eff es b) -> EffectHandler e handlerEs -> Eff (e : es) a -> Eff es b -- | Replace the handler of an existing effect with a new one. -- -- Note: this function allows for augmenting handlers with a new -- functionality as the new handler can send operations to the old one. -- --
-- >>> :{
-- data E :: Effect where
-- Op :: E m ()
-- type instance DispatchOf E = Dynamic
-- :}
--
--
--
-- >>> :{
-- runE :: IOE :> es => Eff (E : es) a -> Eff es a
-- runE = interpret $ \_ Op -> liftIO (putStrLn "op")
-- :}
--
--
-- -- >>> runEff . runE $ send Op -- op ---- --
-- >>> :{
-- augmentE :: (E :> es, IOE :> es) => Eff es a -> Eff es a
-- augmentE = interpose $ \_ Op -> liftIO (putStrLn "augmented op") >> send Op
-- :}
--
--
-- -- >>> runEff . runE . augmentE $ send Op -- augmented op -- op --interpose :: forall e es a. (DispatchOf e ~ Dynamic, e :> es) => EffectHandler e es -> Eff es a -> Eff es a -- | Replace the handler of an existing effect with a new one that uses -- other, private effects. -- --
-- interpose ≡ impose id --impose :: forall e es handlerEs a b. (DispatchOf e ~ Dynamic, e :> es) => (Eff handlerEs a -> Eff es b) -> EffectHandler e handlerEs -> Eff es a -> Eff es b -- | Opaque representation of the Eff environment at the point of -- calling the send function, i.e. right before the control is -- passed to the effect handler. -- -- The second type variable represents effects of a handler and is needed -- for technical reasons to guarantee soundness (see SharedSuffix -- for more information). data LocalEnv (localEs :: [Effect]) (handlerEs :: [Effect]) -- | Create a local unlifting function with the SeqUnlift strategy. -- For the general version see localUnlift. localSeqUnlift :: (HasCallStack, SharedSuffix es handlerEs) => LocalEnv localEs handlerEs -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a) -> Eff es a -- | Create a local unlifting function with the SeqUnlift strategy. -- For the general version see localUnliftIO. localSeqUnliftIO :: (HasCallStack, SharedSuffix es handlerEs, IOE :> es) => LocalEnv localEs handlerEs -> ((forall r. Eff localEs r -> IO r) -> IO a) -> Eff es a -- | Create a local unlifting function with the given strategy. localUnlift :: (HasCallStack, SharedSuffix es handlerEs) => LocalEnv localEs handlerEs -> UnliftStrategy -> ((forall r. Eff localEs r -> Eff es r) -> Eff es a) -> Eff es a -- | Create a local unlifting function with the given strategy. localUnliftIO :: (HasCallStack, SharedSuffix es handlerEs, IOE :> es) => LocalEnv localEs handlerEs -> UnliftStrategy -> ((forall r. Eff localEs r -> IO r) -> IO a) -> Eff es a -- | Create a local lifting function with the SeqUnlift strategy. -- For the general version see localLift. localSeqLift :: (HasCallStack, SharedSuffix es handlerEs) => LocalEnv localEs handlerEs -> ((forall r. Eff es r -> Eff localEs r) -> Eff es a) -> Eff es a -- | Create a local lifting function with the given strategy. localLift :: (HasCallStack, SharedSuffix es handlerEs) => LocalEnv localEs handlerEs -> UnliftStrategy -> ((forall r. Eff es r -> Eff localEs r) -> Eff es a) -> Eff es a -- | Utility for lifting Eff computations of type -- --
-- Eff es a -> Eff es b ---- -- to -- --
-- Eff localEs a -> Eff localEs b ---- -- Note: the computation must not run its argument in a different -- thread, attempting to do so will result in a runtime error. withLiftMap :: (HasCallStack, SharedSuffix es handlerEs) => LocalEnv localEs handlerEs -> ((forall a b. (Eff es a -> Eff es b) -> Eff localEs a -> Eff localEs b) -> Eff es r) -> Eff es r -- | Utility for lifting IO computations of type -- --
-- IO a -> IO b ---- -- to -- --
-- Eff localEs a -> Eff localEs b ---- -- Note: the computation must not run its argument in a different -- thread, attempting to do so will result in a runtime error. -- -- Useful e.g. for lifting the unmasking function in mask-like -- computations: -- --
-- >>> :{
-- data Fork :: Effect where
-- ForkWithUnmask :: ((forall a. m a -> m a) -> m ()) -> Fork m ThreadId
-- type instance DispatchOf Fork = Dynamic
-- :}
--
--
--
-- >>> :{
-- runFork :: IOE :> es => Eff (Fork : es) a -> Eff es a
-- runFork = interpret $ \env (ForkWithUnmask m) -> withLiftMapIO env $ \liftMap -> do
-- localUnliftIO env (ConcUnlift Ephemeral $ Limited 1) $ \unlift -> do
-- forkIOWithUnmask $ \unmask -> unlift $ m $ liftMap unmask
-- :}
--
withLiftMapIO :: (HasCallStack, SharedSuffix es handlerEs, IOE :> es) => LocalEnv localEs handlerEs -> ((forall a b. (IO a -> IO b) -> Eff localEs a -> Eff localEs b) -> Eff es r) -> Eff es r
-- | Create a local lifting and unlifting function with the given strategy.
--
-- Useful for lifting complicated Eff computations where the
-- monadic action shows in both positive (as a result) and negative (as
-- an argument) position.
--
-- Note: depending on the computation you're lifting
-- localUnlift along with withLiftMap might be enough and
-- is more efficient.
localLiftUnlift :: (HasCallStack, SharedSuffix es handlerEs) => LocalEnv localEs handlerEs -> UnliftStrategy -> ((forall r. Eff es r -> Eff localEs r) -> (forall r. Eff localEs r -> Eff es r) -> Eff es a) -> Eff es a
-- | Create a local unlifting function with the given strategy along with
-- an unrestricted lifting function.
--
-- Useful for lifting complicated IO computations where the
-- monadic action shows in both positive (as a result) and negative (as
-- an argument) position.
--
-- Note: depending on the computation you're lifting
-- localUnliftIO along with withLiftMapIO might be enough
-- and is more efficient.
localLiftUnliftIO :: (HasCallStack, SharedSuffix es handlerEs, IOE :> es) => LocalEnv localEs handlerEs -> UnliftStrategy -> ((forall r. IO r -> Eff localEs r) -> (forall r. Eff localEs r -> IO r) -> IO a) -> Eff es a
-- | Require that both effect stacks share an opaque suffix.
--
-- Functions from the localUnlift family utilize this constraint
-- to guarantee sensible usage of unlifting functions.
--
-- As an example, consider the following higher order effect:
--
--
-- >>> :{
-- data E :: Effect where
-- E :: m a -> E m a
-- type instance DispatchOf E = Dynamic
-- :}
--
--
-- Running local actions in a more specific environment is fine:
--
--
-- >>> :{
-- runE1 :: Eff (E ': es) a -> Eff es a
-- runE1 = interpret $ \env -> \case
-- E m -> runReader () $ do
-- localSeqUnlift env $ \unlift -> unlift m
-- :}
--
--
-- Running local actions in a more general environment is fine:
--
--
-- >>> :{
-- runE2 :: Eff (E ': es) a -> Eff es a
-- runE2 = reinterpret (runReader ()) $ \env -> \case
-- E m -> raise $ do
-- localSeqUnlift env $ \unlift -> unlift m
-- :}
--
--
-- However, running local actions in an unrelated environment is not fine
-- as this would make it possible to run anything within
-- runPureEff:
--
--
-- >>> :{
-- runE3 :: Eff (E ': es) a -> Eff es a
-- runE3 = reinterpret (runReader ()) $ \env -> \case
-- E m -> pure . runPureEff $ do
-- localSeqUnlift env $ \unlift -> unlift m
-- :}
-- ...
-- ...Could not deduce (SharedSuffix '[] es)...
-- ...
--
--
-- Running local actions in a monomorphic effect stack is also not fine
-- as this makes a special case of the above possible:
--
--
-- >>> :{
-- runE4 :: Eff '[E, IOE] a -> Eff '[IOE] a
-- runE4 = interpret $ \env -> \case
-- E m -> pure . runPureEff $ do
-- localSeqUnlift env $ \unlift -> unlift m
-- :}
-- ...
-- ...Running local actions in monomorphic effect stacks is not supported...
-- ...
--
class SharedSuffix (es1 :: [Effect]) (es2 :: [Effect])
-- | Request a CallStack.
--
-- NOTE: The implicit parameter ?callStack :: CallStack is an
-- implementation detail and should not be considered part of the
-- CallStack API, we may decide to change the implementation in
-- the future.
type HasCallStack = ?callStack :: CallStack
instance Effectful.Dispatch.Dynamic.SharedSuffix es es
instance Effectful.Dispatch.Dynamic.SharedSuffix es1 es2 => Effectful.Dispatch.Dynamic.SharedSuffix (e : es1) es2
instance Effectful.Dispatch.Dynamic.SharedSuffix es1 es2 => Effectful.Dispatch.Dynamic.SharedSuffix es1 (e : es2)
instance (TypeError ...) => Effectful.Dispatch.Dynamic.SharedSuffix '[] '[]
-- | Provider of the MonadFail instance for Eff.
module Effectful.Fail
-- | Provide the ability to use the MonadFail instance of
-- Eff.
data Fail :: Effect
[Fail] :: String -> Fail m a
-- | Run the Fail effect via Error.
runFail :: Eff (Fail : es) a -> Eff es (Either String a)
-- | Run the Fail effect via the MonadFail instance for
-- IO.
runFailIO :: IOE :> es => Eff (Fail : es) a -> Eff es a
-- | The dynamically dispatched variant of the Error effect.
--
-- Note: unless you plan to change interpretations at runtime,
-- it's recommended to use the statically dispatched variant, i.e.
-- Effectful.Error.Static.
module Effectful.Error.Dynamic
-- | Provide the ability to handle errors of type e.
data Error e :: Effect
[ThrowError] :: e -> Error e m a
[CatchError] :: m a -> (CallStack -> e -> m a) -> Error e m a
-- | Handle errors of type e (via Effectful.Error.Static).
runError :: Eff (Error e : es) a -> Eff es (Either (CallStack, e) a)
-- | Handle errors of type e (via Effectful.Error.Static).
-- In case of an error discard the CallStack.
runErrorNoCallStack :: Eff (Error e : es) a -> Eff es (Either e a)
-- | Throw an error of type e.
throwError :: (HasCallStack, Error e :> es) => e -> Eff es a
-- | Handle an error of type e.
catchError :: (HasCallStack, Error e :> es) => Eff es a -> (CallStack -> e -> Eff es a) -> Eff es a
-- | The same as flip catchError, which is useful in
-- situations where the code for the handler is shorter.
handleError :: Error e :> es => (CallStack -> e -> Eff es a) -> Eff es a -> Eff es a
-- | Similar to catchError, but returns an Either result
-- which is a Right if no error was thrown and a Left
-- otherwise.
tryError :: (HasCallStack, Error e :> es) => Eff es a -> Eff es (Either (CallStack, e) a)
-- | Request a CallStack.
--
-- NOTE: The implicit parameter ?callStack :: CallStack is an
-- implementation detail and should not be considered part of the
-- CallStack API, we may decide to change the implementation in
-- the future.
type HasCallStack = ?callStack :: CallStack
-- | CallStacks are a lightweight method of obtaining a partial
-- call-stack at any point in the program.
--
-- A function can request its call-site with the HasCallStack
-- constraint. For example, we can define
--
-- -- putStrLnWithCallStack :: HasCallStack => String -> IO () ---- -- as a variant of putStrLn that will get its call-site and -- print it, along with the string given as argument. We can access the -- call-stack inside putStrLnWithCallStack with -- callStack. -- --
-- >>> :{
-- putStrLnWithCallStack :: HasCallStack => String -> IO ()
-- putStrLnWithCallStack msg = do
-- putStrLn msg
-- putStrLn (prettyCallStack callStack)
-- :}
--
--
-- Thus, if we call putStrLnWithCallStack we will get a
-- formatted call-stack alongside our string.
--
-- -- >>> putStrLnWithCallStack "hello" -- hello -- CallStack (from HasCallStack): -- putStrLnWithCallStack, called at <interactive>:... in interactive:Ghci... ---- -- GHC solves HasCallStack constraints in three steps: -- --
-- putStrLnWithCallStack :: HasCallStack => String -> IO () ---- -- as a variant of putStrLn that will get its call-site and -- print it, along with the string given as argument. We can access the -- call-stack inside putStrLnWithCallStack with -- callStack. -- --
-- >>> :{
-- putStrLnWithCallStack :: HasCallStack => String -> IO ()
-- putStrLnWithCallStack msg = do
-- putStrLn msg
-- putStrLn (prettyCallStack callStack)
-- :}
--
--
-- Thus, if we call putStrLnWithCallStack we will get a
-- formatted call-stack alongside our string.
--
-- -- >>> putStrLnWithCallStack "hello" -- hello -- CallStack (from HasCallStack): -- putStrLnWithCallStack, called at <interactive>:... in interactive:Ghci... ---- -- GHC solves HasCallStack constraints in three steps: -- --
-- asks f ≡ f <$> ask --asks :: Reader r :> es => (r -> a) -> Eff es a -- | Execute a computation in a modified environment. -- --
-- runReader r (local f m) ≡ runReader (f r) m --local :: Reader r :> es => (r -> r) -> Eff es a -> Eff es a -- | The dynamically dispatched variant of the Reader effect. -- -- Note: unless you plan to change interpretations at runtime, -- it's recommended to use the statically dispatched variant, i.e. -- Effectful.Reader.Static. module Effectful.Reader.Dynamic data Reader r :: Effect [Ask] :: Reader r m r [Local] :: (r -> r) -> m a -> Reader r m a -- | Run the Reader effect with the given initial environment (via -- Effectful.Reader.Static). runReader :: r -> Eff (Reader r : es) a -> Eff es a -- | Execute a computation in a modified environment. withReader :: (r1 -> r2) -> Eff (Reader r2 : es) a -> Eff (Reader r1 : es) a -- | Fetch the value of the environment. ask :: (HasCallStack, Reader r :> es) => Eff es r -- | Retrieve a function of the current environment. -- --
-- asks f ≡ f <$> ask --asks :: (HasCallStack, Reader r :> es) => (r -> a) -> Eff es a -- | Execute a computation in a modified environment. -- --
-- runReader r (local f m) ≡ runReader (f r) m --local :: (HasCallStack, Reader r :> es) => (r -> r) -> Eff es a -> Eff es a -- | Support for access to a mutable value of a particular type. -- -- The value is thread local. If you want it to be shared between -- threads, use Effectful.State.Static.Shared. -- -- Note: unlike the StateT monad transformer from the -- transformers library, the State effect doesn't discard -- state updates when an exception is received: -- --
-- >>> import qualified Control.Monad.Trans.State.Strict as S ---- --
-- >>> :{
-- (`S.execStateT` "Hi") . handle (\(_::ErrorCall) -> pure ()) $ do
-- S.modify (++ " there!")
-- error "oops"
-- :}
-- "Hi"
--
--
--
-- >>> :{
-- runEff . execState "Hi" . handle (\(_::ErrorCall) -> pure ()) $ do
-- modify (++ " there!")
-- error "oops"
-- :}
-- "Hi there!"
--
module Effectful.State.Static.Local
-- | Provide access to a strict (WHNF), thread local, mutable value of type
-- s.
data State s :: Effect
-- | Run the State effect with the given initial state and return
-- the final value along with the final state.
runState :: s -> Eff (State s : es) a -> Eff es (a, s)
-- | Run the State effect with the given initial state and return
-- the final value, discarding the final state.
evalState :: s -> Eff (State s : es) a -> Eff es a
-- | Run the State effect with the given initial state and return
-- the final state, discarding the final value.
execState :: s -> Eff (State s : es) a -> Eff es s
-- | Fetch the current value of the state.
get :: State s :> es => Eff es s
-- | Get a function of the current state.
--
-- -- gets f ≡ f <$> get --gets :: State s :> es => (s -> a) -> Eff es a -- | Set the current state to the given value. put :: State s :> es => s -> Eff es () -- | Apply the function to the current state and return a value. state :: State s :> es => (s -> (a, s)) -> Eff es a -- | Apply the function to the current state. -- --
-- modify f ≡ state (\s -> ((), f s)) --modify :: State s :> es => (s -> s) -> Eff es () -- | Apply the monadic function to the current state and return a value. stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a -- | Apply the monadic function to the current state. -- --
-- modifyM f ≡ stateM (\s -> ((), ) <$> f s) --modifyM :: State s :> es => (s -> Eff es s) -> Eff es () -- | Support for access to a shared, mutable value of a particular type. -- -- The value is shared between multiple threads. If you want each thead -- to manage its own version of the value, use -- Effectful.State.Static.Local. -- -- Note: unlike the StateT monad transformer from the -- transformers library, the State effect doesn't discard -- state updates when an exception is received: -- --
-- >>> import qualified Control.Monad.Trans.State.Strict as S ---- --
-- >>> :{
-- (`S.execStateT` "Hi") . handle (\(_::ErrorCall) -> pure ()) $ do
-- S.modify (++ " there!")
-- error "oops"
-- :}
-- "Hi"
--
--
--
-- >>> :{
-- runEff . execState "Hi" . handle (\(_::ErrorCall) -> pure ()) $ do
-- modify (++ " there!")
-- error "oops"
-- :}
-- "Hi there!"
--
module Effectful.State.Static.Shared
-- | Provide access to a strict (WHNF), shared, mutable value of type
-- s.
data State s :: Effect
-- | Run the State effect with the given initial state and return
-- the final value along with the final state.
runState :: s -> Eff (State s : es) a -> Eff es (a, s)
-- | Run the State effect with the given initial state and return
-- the final value, discarding the final state.
evalState :: s -> Eff (State s : es) a -> Eff es a
-- | Run the State effect with the given initial state and return
-- the final state, discarding the final value.
execState :: s -> Eff (State s : es) a -> Eff es s
-- | Run the State effect with the given initial state MVar
-- and return the final value along with the final state.
runStateMVar :: MVar s -> Eff (State s : es) a -> Eff es (a, s)
-- | Run the State effect with the given initial state MVar
-- and return the final value, discarding the final state.
evalStateMVar :: MVar s -> Eff (State s : es) a -> Eff es a
-- | Run the State effect with the given initial state MVar
-- and return the final state, discarding the final value.
execStateMVar :: MVar s -> Eff (State s : es) a -> Eff es s
-- | Fetch the current value of the state.
get :: State s :> es => Eff es s
-- | Get a function of the current state.
--
-- -- gets f ≡ f <$> get --gets :: State s :> es => (s -> a) -> Eff es a -- | Set the current state to the given value. put :: State s :> es => s -> Eff es () -- | Apply the function to the current state and return a value. -- -- Note: this function gets an exclusive access to the state for -- its duration. state :: State s :> es => (s -> (a, s)) -> Eff es a -- | Apply the function to the current state. -- --
-- modify f ≡ state (\s -> ((), f s)) ---- -- Note: this function gets an exclusive access to the state for -- its duration. modify :: State s :> es => (s -> s) -> Eff es () -- | Apply the monadic function to the current state and return a value. -- -- Note: this function gets an exclusive access to the state for -- its duration. stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a -- | Apply the monadic function to the current state. -- --
-- modifyM f ≡ stateM (\s -> ((), ) <$> f s) ---- -- Note: this function gets an exclusive access to the state for -- its duration. modifyM :: State s :> es => (s -> Eff es s) -> Eff es () -- | The dynamically dispatched variant of the State effect. -- -- Note: unless you plan to change interpretations at runtime, -- it's recommended to use one of the statically dispatched variants, -- i.e. Effectful.State.Static.Local or -- Effectful.State.Static.Shared. module Effectful.State.Dynamic -- | Provide access to a mutable value of type s. data State s :: Effect [Get] :: State s m s [Put] :: s -> State s m () [State] :: (s -> (a, s)) -> State s m a [StateM] :: (s -> m (a, s)) -> State s m a -- | Run the State effect with the given initial state and return -- the final value along with the final state (via -- Effectful.State.Static.Local). runStateLocal :: s -> Eff (State s : es) a -> Eff es (a, s) -- | Run the State effect with the given initial state and return -- the final value, discarding the final state (via -- Effectful.State.Static.Local). evalStateLocal :: s -> Eff (State s : es) a -> Eff es a -- | Run the State effect with the given initial state and return -- the final state, discarding the final value (via -- Effectful.State.Static.Local). execStateLocal :: s -> Eff (State s : es) a -> Eff es s -- | Run the State effect with the given initial state and return -- the final value along with the final state (via -- Effectful.State.Static.Shared). runStateShared :: s -> Eff (State s : es) a -> Eff es (a, s) -- | Run the State effect with the given initial state and return -- the final value, discarding the final state (via -- Effectful.State.Static.Shared). evalStateShared :: s -> Eff (State s : es) a -> Eff es a -- | Run the State effect with the given initial state and return -- the final state, discarding the final value (via -- Effectful.State.Static.Shared). execStateShared :: s -> Eff (State s : es) a -> Eff es s -- | Fetch the current value of the state. get :: (HasCallStack, State s :> es) => Eff es s -- | Get a function of the current state. -- --
-- gets f ≡ f <$> get --gets :: (HasCallStack, State s :> es) => (s -> a) -> Eff es a -- | Set the current state to the given value. put :: (HasCallStack, State s :> es) => s -> Eff es () -- | Apply the function to the current state and return a value. state :: (HasCallStack, State s :> es) => (s -> (a, s)) -> Eff es a -- | Apply the function to the current state. -- --
-- modify f ≡ state (\s -> ((), f s)) --modify :: (HasCallStack, State s :> es) => (s -> s) -> Eff es () -- | Apply the monadic function to the current state and return a value. stateM :: (HasCallStack, State s :> es) => (s -> Eff es (a, s)) -> Eff es a -- | Apply the monadic function to the current state. -- --
-- modifyM f ≡ stateM (\s -> ((), ) <$> f s) --modifyM :: (HasCallStack, State s :> es) => (s -> Eff es s) -> Eff es () -- | Support for access to a write only value of a particular type. -- -- The value is thread local. If you want it to be shared between -- threads, use Effectful.Writer.Static.Shared. -- -- Warning: Writer's state will be accumulated via -- left-associated uses of <>, which makes it -- unsuitable for use with types for which such pattern is inefficient. -- This applies, in particular, to the standard list type, which -- makes the Writer effect pretty niche. -- -- Note: while the Writer from the transformers -- package includes additional operations pass and censor, -- they don't cooperate with runtime exceptions very well, so they're -- deliberately omitted here. module Effectful.Writer.Static.Local -- | Provide access to a strict (WHNF), thread local, write only value of -- type w. data Writer w :: Effect -- | Run a Writer effect and return the final value along with the -- final output. runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w) -- | Run a Writer effect and return the final output, discarding the -- final value. execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w -- | Append the given output to the overall output of the Writer. tell :: (Writer w :> es, Monoid w) => w -> Eff es () -- | Execute an action and append its output to the overall output of the -- Writer. -- -- Note: if an exception is received while the action is executed, -- the partial output of the action will still be appended to the overall -- output of the Writer: -- --
-- >>> :{
-- runEff . execWriter @String $ do
-- tell "Hi"
-- handle (\(_::ErrorCall) -> pure ((), "")) $ do
-- tell " there"
-- listen $ do
-- tell "!"
-- error "oops"
-- :}
-- "Hi there!"
--
listen :: (Writer w :> es, Monoid w) => Eff es a -> Eff es (a, w)
-- | Execute an action and append its output to the overall output of the
-- Writer, then return the final value along with a function of
-- the recorded output.
--
-- -- listens f m ≡ second f <$> listen m --listens :: (Writer w :> es, Monoid w) => (w -> b) -> Eff es a -> Eff es (a, b) -- | Support for access to a write only value of a particular type. -- -- The value is shared between multiple threads. If you want each thead -- to manage its own version of the value, use -- Effectful.Writer.Static.Local. -- -- Warning: Writer's state will be accumulated via -- left-associated uses of <>, which makes it -- unsuitable for use with types for which such pattern is inefficient. -- This applies, in particular, to the standard list type, which -- makes the Writer effect pretty niche. -- -- Note: while the Writer from the transformers -- package includes additional operations pass and censor, -- they don't cooperate with runtime exceptions very well, so they're -- deliberately omitted here. module Effectful.Writer.Static.Shared -- | Provide access to a strict (WHNF), shared, write only value of type -- w. data Writer w :: Effect -- | Run a Writer effect and return the final value along with the -- final output. runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w) -- | Run a Writer effect and return the final output, discarding the -- final value. execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w -- | Append the given output to the overall output of the Writer. tell :: (Writer w :> es, Monoid w) => w -> Eff es () -- | Execute an action and append its output to the overall output of the -- Writer. -- -- Note: if an exception is received while the action is executed, -- the partial output of the action will still be appended to the overall -- output of the Writer: -- --
-- >>> :{
-- runEff . execWriter @String $ do
-- tell "Hi"
-- handle (\(_::ErrorCall) -> pure ((), "")) $ do
-- tell " there"
-- listen $ do
-- tell "!"
-- error "oops"
-- :}
-- "Hi there!"
--
listen :: (Writer w :> es, Monoid w) => Eff es a -> Eff es (a, w)
-- | Execute an action and append its output to the overall output of the
-- Writer, then return the final value along with a function of
-- the recorded output.
--
-- -- listens f m ≡ second f <$> listen m --listens :: (Writer w :> es, Monoid w) => (w -> b) -> Eff es a -> Eff es (a, b) -- | The dynamically dispatched variant of the Writer effect. -- -- Note: unless you plan to change interpretations at runtime, -- it's recommended to use one of the statically dispatched variants, -- i.e. Effectful.Writer.Static.Local or -- Effectful.Writer.Static.Shared. module Effectful.Writer.Dynamic -- | Provide access to a write only value of type w. data Writer w :: Effect [Tell] :: w -> Writer w m () [Listen] :: m a -> Writer w m (a, w) -- | Run the Writer effect and return the final value along with the -- final output (via Effectful.Writer.Static.Local). runWriterLocal :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w) -- | Run a Writer effect and return the final output, discarding the -- final value (via Effectful.Writer.Static.Local). execWriterLocal :: Monoid w => Eff (Writer w : es) a -> Eff es w -- | Run the Writer effect and return the final value along with the -- final output (via Effectful.Writer.Static.Shared). runWriterShared :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w) -- | Run the Writer effect and return the final output, discarding -- the final value (via Effectful.Writer.Static.Shared). execWriterShared :: Monoid w => Eff (Writer w : es) a -> Eff es w -- | Append the given output to the overall output of the Writer. tell :: (HasCallStack, Writer w :> es) => w -> Eff es () -- | Execute an action and append its output to the overall output of the -- Writer. listen :: (HasCallStack, Writer w :> es) => Eff es a -> Eff es (a, w) -- | Execute an action and append its output to the overall output of the -- Writer, then return the final value along with a function of -- the recorded output. -- --
-- listens f m ≡ second f <$> listen m --listens :: (HasCallStack, Writer w :> es) => (w -> b) -> Eff es a -> Eff es (a, b)