effectful-core-2.5.1.0: An easy to use, performant extensible effects library.
Safe HaskellSafe-Inferred
LanguageHaskell2010

Effectful.Internal.Unlift

Description

Implementation of sequential and concurrent unlifts.

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

Synopsis

Unlifting strategies

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.5.1.0-6xcqkxbvBS08JcWnTI2YC6" '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.

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.5.1.0-6xcqkxbvBS08JcWnTI2YC6" '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.5.1.0-6xcqkxbvBS08JcWnTI2YC6" 'False) (C1 ('MetaCons "Limited" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Int)) :+: C1 ('MetaCons "Unlimited" 'PrefixI 'False) (U1 :: Type -> Type))

Unlifting functions

ephemeralConcUnlift Source #

Arguments

:: (HasCallStack, forall r. Coercible (m r) (Env es -> IO r)) 
=> Env es 
-> Int

Number of permitted uses of the unlift function.

-> ((forall r. m r -> IO r) -> IO a) 
-> IO a 

Concurrent unlift that doesn't preserve the environment between calls to the unlifting function in threads other than its creator.

persistentConcUnlift Source #

Arguments

:: (HasCallStack, forall r. Coercible (m r) (Env es -> IO r)) 
=> Env es 
-> Bool 
-> Int

Number of threads that are allowed to use the unlift function.

-> ((forall r. m r -> IO r) -> IO a) 
-> IO a 

Concurrent unlift that preserves the environment between calls to the unlifting function within a particular thread.