effectful-core
Safe HaskellSafe-Inferred
LanguageGHC2021

Effectful.Internal.Monad

Description

The Eff monad.

This module is intended for internal use only, and may change without warning in subsequent releases.

Synopsis

The Eff monad

data Eff (es :: [Effect]) a Source #

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 (:>):

  • Allows the computation to be used in functions that may perform other effects.
  • Allows the effects to be handled in any order.

Instances

Instances details
IOE :> es => MonadBaseControl IO (Eff es) Source #

Instance included for compatibility with existing code.

Usage of withEffToIO is preferrable as it allows specifying the UnliftStrategy on a case-by-case basis and has better error reporting.

Note: the unlifting strategy for liftBaseWith is taken from the IOE context (see unliftStrategy).

Instance details

Defined in Effectful.Internal.Monad

Associated Types

type StM (Eff es) a

Methods

liftBaseWith :: (RunInBase (Eff es) IO -> IO a) -> Eff es a

restoreM :: StM (Eff es) a -> Eff es a

(Show e, Error e :> es, MonadError e (Eff es)) => MonadError e (Eff es) Source #

Instance included for compatibility with existing code.

Instance details

Defined in Effectful.Internal.Effect.Dynamic

Methods

throwError :: e -> Eff es a #

catchError :: Eff es a -> (e -> Eff es a) -> Eff es a #

(Reader r :> es, MonadReader r (Eff es)) => MonadReader r (Eff es) Source #

Instance included for compatibility with existing code.

Instance details

Defined in Effectful.Internal.Effect.Dynamic

Methods

ask :: Eff es r #

local :: (r -> r) -> Eff es a -> Eff es a #

reader :: (r -> a) -> Eff es a #

(State s :> es, MonadState s (Eff es)) => MonadState s (Eff es) Source #

Instance included for compatibility with existing code.

Instance details

Defined in Effectful.Internal.Effect.Dynamic

Methods

get :: Eff es s #

put :: s -> Eff es () #

state :: (s -> (a, s)) -> Eff es a #

(Monoid w, Writer w :> es, MonadWriter w (Eff es)) => MonadWriter w (Eff es) Source #

Instance included for compatibility with existing code.

Warning: pass is not implemented due to ambiguous semantics in presence of runtime exceptions, so calling it (also indirectly via censor, which is defined in terms of pass) results in a runtime error.

Instance details

Defined in Effectful.Internal.Effect.Dynamic

Methods

writer :: (a, w) -> Eff es a #

tell :: w -> Eff es () #

listen :: Eff es a -> Eff es (a, w) #

pass :: Eff es (a, w -> w) -> Eff es a #

IOE :> es => MonadBase IO (Eff es) Source #

Instance included for compatibility with existing code.

Usage of liftIO is preferrable as it's a standard.

Instance details

Defined in Effectful.Internal.Monad

Methods

liftBase :: IO α -> Eff es α

Fail :> es => MonadFail (Eff es) Source # 
Instance details

Defined in Effectful.Internal.Monad

Methods

fail :: String -> Eff es a #

MonadFix (Eff es) Source # 
Instance details

Defined in Effectful.Internal.Monad

Methods

mfix :: (a -> Eff es a) -> Eff es a #

IOE :> es => MonadIO (Eff es) Source # 
Instance details

Defined in Effectful.Internal.Monad

Methods

liftIO :: IO a -> Eff es a #

NonDet :> es => Alternative (Eff es) Source #

Since: 2.2.0.0

Instance details

Defined in Effectful.Internal.Monad

Methods

empty :: Eff es a #

(<|>) :: Eff es a -> Eff es a -> Eff es a #

some :: Eff es a -> Eff es [a] #

many :: Eff es a -> Eff es [a] #

Applicative (Eff es) Source # 
Instance details

Defined in Effectful.Internal.Monad

Methods

pure :: a -> Eff es a #

(<*>) :: Eff es (a -> b) -> Eff es a -> Eff es b #

liftA2 :: (a -> b -> c) -> Eff es a -> Eff es b -> Eff es c #

(*>) :: Eff es a -> Eff es b -> Eff es b #

(<*) :: Eff es a -> Eff es b -> Eff es a #

Functor (Eff es) Source # 
Instance details

Defined in Effectful.Internal.Monad

Methods

fmap :: (a -> b) -> Eff es a -> Eff es b #

(<$) :: a -> Eff es b -> Eff es a #

Monad (Eff es) Source # 
Instance details

Defined in Effectful.Internal.Monad

Methods

(>>=) :: Eff es a -> (a -> Eff es b) -> Eff es b #

(>>) :: Eff es a -> Eff es b -> Eff es b #

return :: a -> Eff es a #

NonDet :> es => MonadPlus (Eff es) Source #

Since: 2.2.0.0

Instance details

Defined in Effectful.Internal.Monad

Methods

mzero :: Eff es a #

mplus :: Eff es a -> Eff es a -> Eff es a #

MonadCatch (Eff es) Source #

Available without any effect requirements.

This is the one instance of the three that would arguably benefit from requiring IOE (or a more specialized effect), as catching imprecise exceptions makes it possible to write non-deterministic pure functions with runPureEff. Unfortunately it can't, because MonadCatch is a superclass of MonadMask, which needs to be available unconditionally (see the note there).

For the full discussion see issue #76.

Instance details

Defined in Effectful.Internal.Monad

Methods

catch :: (HasCallStack, Exception e) => Eff es a -> (e -> Eff es a) -> Eff es a #

MonadMask (Eff es) Source #

Available without any effect requirements.

This makes it possible to use cleanup functions such as bracket or finally anywhere, e.g. to restore a state on error:

transactionally :: forall s es a. State s :> es => Eff es a -> Eff es a
transactionally = bracketOnError (get @s) (put @s) . const

Requiring IOE would make functions like the above impossible to write and force IOE to show up in application code that otherwise only needs more restricted effects, which would be a significant usability regression. On the other hand, requiring a more specialized effect would be annoying, since functions making use of MonadMask are ubiquitous.

Instance details

Defined in Effectful.Internal.Monad

Methods

mask :: HasCallStack => ((forall a. Eff es a -> Eff es a) -> Eff es b) -> Eff es b #

uninterruptibleMask :: HasCallStack => ((forall a. Eff es a -> Eff es a) -> Eff es b) -> Eff es b #

generalBracket :: HasCallStack => Eff es a -> (a -> ExitCase b -> Eff es c) -> (a -> Eff es b) -> Eff es (b, c) #

MonadThrow (Eff es) Source #

Available without any effect requirements.

Gating it behind an effect (such as IOE or a more specialized effect) would accomplish nothing, since any Haskell expression is free to throw an exception with throw at any point.

Instance details

Defined in Effectful.Internal.Monad

Methods

throwM :: (HasCallStack, Exception e) => e -> Eff es a #

Prim :> es => PrimMonad (Eff es) Source # 
Instance details

Defined in Effectful.Internal.Monad

Associated Types

type PrimState (Eff es)

Methods

primitive :: (State# (PrimState (Eff es)) -> (# State# (PrimState (Eff es)), a #)) -> Eff es a

IOE :> es => MonadUnliftIO (Eff es) Source #

Instance included for compatibility with existing code.

Usage of withEffToIO is preferrable as it allows specifying the UnliftStrategy on a case-by-case basis and has better error reporting.

Note: the unlifting strategy for withRunInIO is taken from the IOE context (see unliftStrategy).

Instance details

Defined in Effectful.Internal.Monad

Methods

withRunInIO :: ((forall a. Eff es a -> IO a) -> IO b) -> Eff es b #

Monoid a => Monoid (Eff es a) Source # 
Instance details

Defined in Effectful.Internal.Monad

Methods

mempty :: Eff es a #

mappend :: Eff es a -> Eff es a -> Eff es a #

mconcat :: [Eff es a] -> Eff es a #

Semigroup a => Semigroup (Eff es a) Source # 
Instance details

Defined in Effectful.Internal.Monad

Methods

(<>) :: Eff es a -> Eff es a -> Eff es a #

sconcat :: NonEmpty (Eff es a) -> Eff es a #

stimes :: Integral b => b -> Eff es a -> Eff es a #

type PrimState (Eff es) Source # 
Instance details

Defined in Effectful.Internal.Monad

type PrimState (Eff es) = PrimStateEff
type StM (Eff es) a Source # 
Instance details

Defined in Effectful.Internal.Monad

type StM (Eff es) a = a

runPureEff :: HasCallStack => Eff '[] a -> a Source #

Run a pure Eff computation.

For running computations with side effects see runEff.

Access to the internal representation

unEff :: Eff es a -> Env es -> IO a Source #

Peel off the constructor of Eff.

unsafeEff :: (Env es -> IO a) -> Eff es a Source #

Access the underlying IO monad along with the environment.

This function is unsafe because it can be used to introduce arbitrary IO actions into pure Eff computations.

unsafeEff_ :: IO a -> Eff es a Source #

Access the underlying IO monad.

This function is unsafe because it can be used to introduce arbitrary IO actions into pure Eff computations.

NonDet

data NonDet :: Effect where Source #

Provide the ability to use the Alternative and MonadPlus instance for Eff.

Note: NonDet does not backtrack. Formally, it obeys the "left-catch" law for MonadPlus, rather than the "left-distribution" law. This means that it behaves more like Maybe than [].

Since: 2.2.0.0

Constructors

Empty :: NonDet m a 
(:<|>:) :: m a -> m a -> NonDet m a 

Instances

Instances details
type DispatchOf NonDet Source # 
Instance details

Defined in Effectful.Internal.Monad

Fail

data Fail :: Effect where Source #

Provide the ability to use the MonadFail instance for Eff.

Constructors

Fail :: String -> Fail m a 

Instances

Instances details
type DispatchOf Fail Source # 
Instance details

Defined in Effectful.Internal.Monad

IO

data IOE :: Effect Source #

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.

Instances

Instances details
type DispatchOf IOE Source # 
Instance details

Defined in Effectful.Internal.Monad

newtype StaticRep IOE Source # 
Instance details

Defined in Effectful.Internal.Monad

runEff :: HasCallStack => Eff '[IOE] a -> IO a Source #

Run an Eff computation with side effects.

For running pure computations see runPureEff.

Prim

data Prim :: Effect Source #

Provide the ability to perform primitive state-transformer actions.

Instances

Instances details
type DispatchOf Prim Source # 
Instance details

Defined in Effectful.Internal.Monad

data StaticRep Prim Source # 
Instance details

Defined in Effectful.Internal.Monad

data PrimStateEff Source #

PrimState token for Eff. Used instead of RealWorld to prevent the Prim effect from executing arbitrary IO actions via ioToPrim.

runPrim :: (HasCallStack, IOE :> es) => Eff (Prim : es) a -> Eff es a Source #

Run an Eff computation with primitive state-transformer actions.

Lifting

raise :: forall e es a. Eff es a -> Eff (e : es) a Source #

Lift an Eff computation into an effect stack with one more effect.

raiseWith Source #

Arguments

:: HasCallStack 
=> UnliftStrategy 
-> ((forall r. Eff (e : es) r -> Eff es r) -> Eff es a)

Continuation with the unlifting function in scope.

-> Eff (e : es) a 

Lift an Eff computation into an effect stack with one more effect and create an unlifting function with the given strategy.

Since: 1.2.0.0

subsume :: e :> es => Eff (e : es) a -> Eff es a Source #

Eliminate a duplicate effect from the top of the effect stack.

inject :: Subset subEs es => Eff subEs a -> Eff es a Source #

Allow for running an effect stack subEs within es as long as subEs is a permutation (with possible duplicates) of a subset of es.

Generalizes raise and subsume.

>>> 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’
...

class KnownPrefix es => Subset (subEs :: [Effect]) (es :: [Effect]) Source #

Provide evidence that subEs is a subset of es.

Instances

Instances details
(KnownPrefix es, IsUnknownSuffixOf subEs es) => Subset subEs es Source # 
Instance details

Defined in Effectful.Internal.Effect

KnownPrefix es => Subset ('[] :: [Effect]) es Source # 
Instance details

Defined in Effectful.Internal.Effect

(e :> es, Subset subEs es) => Subset (e ': subEs) es Source # 
Instance details

Defined in Effectful.Internal.Effect

Unlifting

data UnliftStrategy Source #

The strategy to use when unlifting Eff computations via withEffToIO or the localUnlift family.

Constructors

SeqUnlift

The sequential strategy is the fastest and a default setting for IOE. Any attempt of calling the unlifting function in threads distinct from its creator will result in a runtime error.

SeqForkUnlift

Like SeqUnlift, but all unlifted actions will be executed in a cloned environment.

The main consequence is that thread local state is forked at the point of creation of the unlifting function and its modifications in unlifted actions will not affect the main thread of execution (and vice versa):

>>> import Effectful
>>> import Effectful.State.Dynamic
>>> :{
 action :: (IOE :> es, State Int :> es) => Eff es ()
 action = do
   modify @Int (+1)
   withEffToIO SeqForkUnlift $ \unlift -> unlift $ modify @Int (+2)
   modify @Int (+4)
:}
>>> runEff . execStateLocal @Int 0 $ action
5
>>> runEff . execStateShared @Int 0 $ action
7

Because of this it's possible to safely use the unlifting function outside of the scope of effects it captures, e.g. by creating an IO action that executes effectful operations and running it later:

>>> :{
  delayed :: UnliftStrategy -> IO (IO String)
  delayed strategy = runEff . evalStateLocal "Hey" $ do
    r <- withEffToIO strategy $ \unlift -> pure $ unlift get
    modify (++ "!!!")
    pure r
:}

This doesn't work with the SeqUnlift strategy because when the returned action runs, State is no longer in scope:

>>> join $ delayed SeqUnlift
*** Exception: version (...) /= storageVersion (0)
...

However, it does with the SeqForkUnlift strategy:

>>> join $ delayed SeqForkUnlift
"Hey"
ConcUnlift !Persistence !Limit

The concurrent strategy makes it possible for the unlifting function to be called in threads distinct from its creator. See Persistence and Limit settings for more information.

Instances

Instances details
Generic UnliftStrategy Source # 
Instance details

Defined in Effectful.Internal.Unlift

Associated Types

type Rep UnliftStrategy :: Type -> Type #

Show UnliftStrategy Source # 
Instance details

Defined in Effectful.Internal.Unlift

Eq UnliftStrategy Source # 
Instance details

Defined in Effectful.Internal.Unlift

Ord UnliftStrategy Source # 
Instance details

Defined in Effectful.Internal.Unlift

type Rep UnliftStrategy Source # 
Instance details

Defined in Effectful.Internal.Unlift

type Rep UnliftStrategy = D1 ('MetaData "UnliftStrategy" "Effectful.Internal.Unlift" "effectful-core-2.7.1.1-inplace" 'False) (C1 ('MetaCons "SeqUnlift" 'PrefixI 'False) (U1 :: Type -> Type) :+: (C1 ('MetaCons "SeqForkUnlift" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "ConcUnlift" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Persistence) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Limit))))

data Persistence Source #

Persistence setting for the ConcUnlift strategy.

Different functions require different persistence strategies. Examples:

  • Lifting pooledMapConcurrentlyN from the unliftio library requires the Ephemeral strategy as we don't want jobs to share environment changes made by previous jobs run in the same worker thread.
  • Lifting forkIOWithUnmask requires the Persistent strategy, otherwise the unmasking function would start with a fresh environment each time it's called.

Both cases come down to what happens when the unlifting function is called more than once in the same thread. If a thread calls it only once, the Persistence setting makes no observable difference.

Example 1

Consider a thread that modifies thread local state, then inspects it with a second call to the unlifting function:

>>> import Control.Concurrent
>>> import Control.Monad
>>> import Effectful
>>> import Effectful.State.Dynamic
>>> :{
  modifyThenGet :: UnliftStrategy -> IO Int
  modifyThenGet strategy = runEff . evalStateLocal @Int 0 $ do
    withEffToIO strategy $ \unlift -> do
      result <- newEmptyMVar
      void . forkIO $ do
        unlift $ modify @Int (+1)
        putMVar result =<< unlift (get @Int)
      takeMVar result
:}

With the Persistent strategy the unlifting function keeps the environment between the calls, so the second call sees the modification from the first one:

>>> modifyThenGet $ ConcUnlift Persistent (Limited 1)
1

On the other hand, with Ephemeral each call to the unlifting function starts with a fresh copy of the environment, so the modification is silently lost:

>>> modifyThenGet $ ConcUnlift Ephemeral (Limited 2)
0

This also showcases the limit meaning different things for the two settings: for the Persistent strategy it limits the number of threads the unlifting can happen in, for Ephemeral it limits the number of calls to the unlifting function.

Example 2

Consider a situation where a single worker thread runs multiple independent jobs:

>>> :{
  twoJobs :: UnliftStrategy -> IO [Int]
  twoJobs strategy = runEff . evalStateLocal @Int 0 $ do
    withEffToIO strategy $ \unlift -> do
      result <- newEmptyMVar
      void . forkIO $ do
        let job = unlift $ modify @Int (+1) >> get @Int
        putMVar result =<< sequence [job, job]
      takeMVar result
:}

With Ephemeral both jobs start from the environment as it was when the unlifting function was created:

>>> twoJobs $ ConcUnlift Ephemeral Unlimited
[1,1]

With Persistent the second job inherits changes made by the first one, even though the user would most likely expect them to be independent:

>>> twoJobs $ ConcUnlift Persistent Unlimited
[1,2]

Constructors

Ephemeral

Don't persist the environment between calls to the unlifting function in threads distinct from its creator.

Persistent

Persist the environment between calls to the unlifting function within a particular thread.

Instances

Instances details
Generic Persistence Source # 
Instance details

Defined in Effectful.Internal.Unlift

Associated Types

type Rep Persistence :: Type -> Type #

Show Persistence Source # 
Instance details

Defined in Effectful.Internal.Unlift

Eq Persistence Source # 
Instance details

Defined in Effectful.Internal.Unlift

Ord Persistence Source # 
Instance details

Defined in Effectful.Internal.Unlift

type Rep Persistence Source # 
Instance details

Defined in Effectful.Internal.Unlift

type Rep Persistence = D1 ('MetaData "Persistence" "Effectful.Internal.Unlift" "effectful-core-2.7.1.1-inplace" 'False) (C1 ('MetaCons "Ephemeral" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Persistent" 'PrefixI 'False) (U1 :: Type -> Type))

data Limit Source #

Limit setting for the ConcUnlift strategy.

Constructors

Limited !Int

Behavior dependent on the Persistence setting.

For Ephemeral, it limits the amount of uses of the unlifting function in threads distinct from its creator to N. The unlifting function will create N copies of the environment when called N times and K+1 copies when called K < N times.

For Persistent, it limits the amount of threads, distinct from the creator of the unlifting function, it can be called in to N. The amount of calls to the unlifting function within a particular threads is unlimited. The unlifting function will create N copies of the environment when called in N threads and K+1 copies when called in K < N threads.

Unlimited

Unlimited use of the unlifting function.

Instances

Instances details
Generic Limit Source # 
Instance details

Defined in Effectful.Internal.Unlift

Associated Types

type Rep Limit :: Type -> Type #

Methods

from :: Limit -> Rep Limit x #

to :: Rep Limit x -> Limit #

Show Limit Source # 
Instance details

Defined in Effectful.Internal.Unlift

Methods

showsPrec :: Int -> Limit -> ShowS #

show :: Limit -> String #

showList :: [Limit] -> ShowS #

Eq Limit Source # 
Instance details

Defined in Effectful.Internal.Unlift

Methods

(==) :: Limit -> Limit -> Bool #

(/=) :: Limit -> Limit -> Bool #

Ord Limit Source # 
Instance details

Defined in Effectful.Internal.Unlift

Methods

compare :: Limit -> Limit -> Ordering #

(<) :: Limit -> Limit -> Bool #

(<=) :: Limit -> Limit -> Bool #

(>) :: Limit -> Limit -> Bool #

(>=) :: Limit -> Limit -> Bool #

max :: Limit -> Limit -> Limit #

min :: Limit -> Limit -> Limit #

type Rep Limit Source # 
Instance details

Defined in Effectful.Internal.Unlift

type Rep Limit = D1 ('MetaData "Limit" "Effectful.Internal.Unlift" "effectful-core-2.7.1.1-inplace" 'False) (C1 ('MetaCons "Limited" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Int)) :+: C1 ('MetaCons "Unlimited" 'PrefixI 'False) (U1 :: Type -> Type))

unliftStrategy :: (HasCallStack, IOE :> es) => Eff es UnliftStrategy Source #

Get the current UnliftStrategy.

Note: this strategy is implicitly used by the MonadUnliftIO and MonadBaseControl instance for Eff.

withUnliftStrategy :: (HasCallStack, IOE :> es) => UnliftStrategy -> Eff es a -> Eff es a Source #

Locally override the current UnliftStrategy with the given value.

withSeqEffToIO Source #

Arguments

:: (HasCallStack, IOE :> es) 
=> ((forall r. Eff es r -> IO r) -> IO a)

Continuation with the unlifting function in scope.

-> Eff es a 

Create an unlifting function with the SeqUnlift strategy. For the general version see withEffToIO.

Note: usage of this function is preferrable to withRunInIO because of explicit unlifting strategy and better error reporting.

Since: 2.2.2.0

withEffToIO Source #

Arguments

:: (HasCallStack, IOE :> es) 
=> UnliftStrategy 
-> ((forall r. Eff es r -> IO r) -> IO a)

Continuation with the unlifting function in scope.

-> Eff es a 

Create an unlifting function with the given strategy.

Note: usage of this function is preferrable to withRunInIO because of explicit unlifting strategy and better error reporting.

reallyUnsafeLiftMapIO :: (IO a -> IO b) -> Eff es a -> Eff es b Source #

Utility for lifting IO computations of type

IO a -> IO b

to

Eff es a -> Eff es b

This function is really unsafe because:

  • It can be used to introduce arbitrary IO actions into pure Eff computations.
  • The IO computation must run its argument in a way that's perceived as sequential to the outside observer, e.g. in the same thread or in a worker thread that finishes before the argument is run again.

Warning: if you disregard the second point, you will experience weird bugs, data races or internal consistency check failures.

When in doubt, use unsafeLiftMapIO, especially since this version saves only a simple safety check per call of reallyUnsafeLiftMapIO f.

reallyUnsafeUnliftIO :: ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a Source #

Create an unlifting function.

This function is really unsafe because:

  • It can be used to introduce arbitrary IO actions into pure Eff computations.
  • Unlifted Eff computations must be run in a way that's perceived as sequential to the outside observer, e.g. in the same thread as the caller of reallyUnsafeUnliftIO or in a worker thread that finishes before another unlifted computation is run.

Warning: if you disregard the second point, you will experience weird bugs, data races or internal consistency check failures.

When in doubt, use unsafeSeqUnliftIO, especially since this version saves only a simple safety check per call of the unlifting function.

Low-level unlifts

seqUnliftIO Source #

Arguments

:: HasCallStack 
=> Env es

The environment.

-> ((forall r. Eff es r -> IO r) -> IO a)

Continuation with the unlifting function in scope.

-> IO a 

Create an unlifting function with the SeqUnlift strategy.

seqForkUnliftIO Source #

Arguments

:: HasCallStack 
=> Env es

The environment.

-> ((forall r. Eff es r -> IO r) -> IO a)

Continuation with the unlifting function in scope.

-> IO a 

Create an unlifting function with the SeqForkUnlift strategy.

concUnliftIO Source #

Arguments

:: HasCallStack 
=> Env es

The environment.

-> Persistence 
-> Limit 
-> ((forall r. Eff es r -> IO r) -> IO a)

Continuation with the unlifting function in scope.

-> IO a 

Create an unlifting function with the ConcUnlift strategy.

seqForkUnliftsIO Source #

Arguments

:: HasCallStack 
=> Env es 
-> Env localEs 
-> ((forall r. Eff es r -> IO r) -> (forall r. Eff localEs r -> IO r) -> IO a)

Continuation with the unlifting functions in scope.

-> IO a 

Create two unlifting functions with the SeqForkUnlift strategy.

The unlifting functions will share the effect storage (unlike with two separate calls to seqForkUnliftIO).

Warning: both environments must have the same underlying storage.

Since: 2.7.0.0

concUnliftsIO Source #

Arguments

:: HasCallStack 
=> Env es 
-> Env localEs

The environment.

-> Persistence 
-> Limit 
-> ((forall r. Eff es r -> IO r) -> (forall r. Eff localEs r -> IO r) -> IO a)

Continuation with the unlifting functions in scope.

-> IO a 

Create unlifting functions with the ConcUnlift strategy.

In the Persistent variant the unlifting functions will share the effect storage in each thread (unlike with two separate calls to concUnliftIO).

Warning: both environments must have the same underlying storage.

Since: 2.7.0.0

Dispatch

Dynamic dispatch

type EffectHandler (e :: Effect) (es :: [Effect]) Source #

Arguments

 = forall a localEs. (HasCallStack, e :> localEs) 
=> LocalEnv localEs

Capture of the local environment for handling local Eff computations when e is a higher order effect.

-> e (Eff localEs) a

The operation.

-> Eff es a 

Type signature of the effect handler.

data LocalEnv (localEs :: [Effect]) Source #

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.

Note: functions that consume it perform runtime checks to ensure that it's used within the scope of the effect handler it belongs to.

unwrapLocalEnv :: HasCallStack => Env es -> LocalEnv localEs -> IO (Env localEs) Source #

requireMatchingStorages :: HasCallStack => Env es -> LocalEnv localEs -> IO () Source #

Make sure that the LocalEnv is used in the thread/context of the effect handler it belongs to.

data Handler :: Effect -> Type where Source #

An internal representation of dynamically dispatched effects, i.e. the effect handler bundled with its environment.

Constructors

Handler :: !(Env handlerEs) -> !(HandlerImpl e handlerEs) -> Handler e 

newtype HandlerImpl e es Source #

Wrapper to prevent a space leak on reconstruction of Handler in relinkHandler (see https://gitlab.haskell.org/ghc/ghc/-/issues/25520).

Constructors

HandlerImpl (EffectHandler e es) 

runHandler :: (HasCallStack, DispatchOf e ~ Dynamic) => Handler e -> Eff (e : es) a -> Eff es a Source #

Run a dynamically dispatched effect with the given handler.

send Source #

Arguments

:: (HasCallStack, DispatchOf e ~ Dynamic, e :> es) 
=> e (Eff es) a

The operation.

-> Eff es a 

Send an operation of the given effect to its handler for execution.

Static dispatch

data family StaticRep (e :: Effect) :: Type Source #

Internal representations of statically dispatched effects.

Instances

Instances details
newtype StaticRep IOE Source # 
Instance details

Defined in Effectful.Internal.Monad

data StaticRep Prim Source # 
Instance details

Defined in Effectful.Internal.Monad

newtype StaticRep (Error e) Source # 
Instance details

Defined in Effectful.Error.Static

newtype StaticRep (Error e) = Error ErrorId
newtype StaticRep (Input i) Source # 
Instance details

Defined in Effectful.Input.Static

newtype StaticRep (Input i) = Input i
data StaticRep (Input i) Source # 
Instance details

Defined in Effectful.Input.Static.Action

data StaticRep (Input i) where
data StaticRep (Output o) Source # 
Instance details

Defined in Effectful.Output.Static.Action

data StaticRep (Output o) where
data StaticRep (Output o) Source # 
Instance details

Defined in Effectful.Output.Static.Local.Array

data StaticRep (Output o) = Output !Int !(MutableArray RealWorld o)
newtype StaticRep (Output o) Source # 
Instance details

Defined in Effectful.Output.Static.Local.List

newtype StaticRep (Output o) = Output [o]
newtype StaticRep (Output o) Source # 
Instance details

Defined in Effectful.Output.Static.Shared.Array

newtype StaticRep (Output o) = Output (MVar (OutputData o))
newtype StaticRep (Output o) Source # 
Instance details

Defined in Effectful.Output.Static.Shared.List

newtype StaticRep (Output o) = Output (MVar [o])
newtype StaticRep (Reader r) Source # 
Instance details

Defined in Effectful.Reader.Static

newtype StaticRep (Reader r) = Reader r
newtype StaticRep (ReturnWith r) Source # 
Instance details

Defined in Effectful.ReturnWith.Static

newtype StaticRep (ReturnWith r) = ReturnWith ReturnWithId
newtype StaticRep (State s) Source # 
Instance details

Defined in Effectful.State.Static.Local

newtype StaticRep (State s) = State s
newtype StaticRep (State s) Source # 
Instance details

Defined in Effectful.State.Static.Shared

newtype StaticRep (State s) = State (MVar s)
newtype StaticRep (Writer w) Source # 
Instance details

Defined in Effectful.Writer.Static.Local

newtype StaticRep (Writer w) = Writer w
newtype StaticRep (Writer w) Source # 
Instance details

Defined in Effectful.Writer.Static.Shared

newtype StaticRep (Writer w) = Writer (MVar w)
data StaticRep (Labeled label e) Source # 
Instance details

Defined in Effectful.Labeled

data StaticRep (Labeled label e)

type family MaybeIOE (sideEffects :: SideEffects) (es :: [Effect]) :: Constraint where ... Source #

Require the IOE effect for running statically dispatched effects whose operations perform side effects.

runStaticRep Source #

Arguments

:: (HasCallStack, DispatchOf e ~ Static sideEffects, MaybeIOE sideEffects es) 
=> StaticRep e

The initial representation.

-> Eff (e : es) a 
-> Eff es (a, StaticRep e) 

Run a statically dispatched effect with the given initial representation and return the final value along with the final representation.

evalStaticRep Source #

Arguments

:: (HasCallStack, DispatchOf e ~ Static sideEffects, MaybeIOE sideEffects es) 
=> StaticRep e

The initial representation.

-> Eff (e : es) a 
-> Eff es a 

Run a statically dispatched effect with the given initial representation and return the final value, discarding the final representation.

execStaticRep Source #

Arguments

:: (HasCallStack, DispatchOf e ~ Static sideEffects, MaybeIOE sideEffects es) 
=> StaticRep e

The initial representation.

-> Eff (e : es) a 
-> Eff es (StaticRep e) 

Run a statically dispatched effect with the given initial representation and return the final representation, discarding the final value.

getStaticRep :: (HasCallStack, DispatchOf e ~ Static sideEffects, e :> es) => Eff es (StaticRep e) Source #

Fetch the current representation of the effect.

putStaticRep :: (HasCallStack, DispatchOf e ~ Static sideEffects, e :> es) => StaticRep e -> Eff es () Source #

Set the current representation of the effect to the given value.

stateStaticRep Source #

Arguments

:: (HasCallStack, DispatchOf e ~ Static sideEffects, e :> es) 
=> (StaticRep e -> (a, StaticRep e))

The function to modify the representation.

-> Eff es a 

Apply the function to the current representation of the effect and return a value.

stateStaticRepM Source #

Arguments

:: (HasCallStack, DispatchOf e ~ Static sideEffects, e :> es) 
=> (StaticRep e -> Eff es (a, StaticRep e))

The function to modify the representation.

-> Eff es a 

Apply the monadic function to the current representation of the effect and return a value.

localStaticRep Source #

Arguments

:: (HasCallStack, DispatchOf e ~ Static sideEffects, e :> es) 
=> (StaticRep e -> StaticRep e)

The function to temporarily modify the representation.

-> Eff es a 
-> Eff es a 

Execute a computation with a temporarily modified representation of the effect.