| Safe Haskell | Safe-Inferred |
|---|---|
| Language | GHC2021 |
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
- data UnliftStrategy
- data Persistence
- data Limit
- ephemeralConcLimitedUnlift :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r)) => Env es -> Int -> ((forall r. effEs r -> IO r) -> IO a) -> IO a
- ephemeralConcUnlimitedUnlift :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r)) => Env es -> ((forall r. effEs r -> IO r) -> IO a) -> IO a
- persistentConcUnlift :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r)) => Env es -> Bool -> Int -> ((forall r. effEs r -> IO r) -> IO a) -> IO a
- persistentConcSingleUnlift :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r)) => Env es -> ((forall r. effEs r -> IO r) -> IO a) -> IO a
- persistentConcUnlifts :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r), forall r. Coercible (effLocalEs r) (Env localEs -> IO r)) => Env es -> Env localEs -> Bool -> Int -> ((forall r. effEs r -> IO r) -> (forall r. effLocalEs r -> IO r) -> IO a) -> IO a
- persistentConcSingleUnlifts :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r), forall r. Coercible (effLocalEs r) (Env localEs -> IO r)) => Env es -> Env localEs -> ((forall r. effEs r -> IO r) -> (forall r. effLocalEs r -> IO r) -> IO a) -> IO a
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
|
| SeqForkUnlift | Like 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):
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
This doesn't work with the
However, it does with the
|
| ConcUnlift !Persistence !Limit | The concurrent strategy makes it possible for the unlifting function to
be called in threads distinct from its creator. See |
Instances
data Persistence Source #
Persistence setting for the ConcUnlift strategy.
Different functions require different persistence strategies. Examples:
- Lifting
pooledMapConcurrentlyNfrom theunliftiolibrary requires theEphemeralstrategy as we don't want jobs to share environment changes made by previous jobs run in the same worker thread. - Lifting
forkIOWithUnmaskrequires thePersistentstrategy, 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
| Generic Persistence Source # | |
Defined in Effectful.Internal.Unlift Associated Types type Rep Persistence :: Type -> Type # | |
| Show Persistence Source # | |
Defined in Effectful.Internal.Unlift Methods showsPrec :: Int -> Persistence -> ShowS # show :: Persistence -> String # showList :: [Persistence] -> ShowS # | |
| Eq Persistence Source # | |
Defined in Effectful.Internal.Unlift | |
| Ord Persistence Source # | |
Defined in Effectful.Internal.Unlift Methods compare :: Persistence -> Persistence -> Ordering # (<) :: Persistence -> Persistence -> Bool # (<=) :: Persistence -> Persistence -> Bool # (>) :: Persistence -> Persistence -> Bool # (>=) :: Persistence -> Persistence -> Bool # max :: Persistence -> Persistence -> Persistence # min :: Persistence -> Persistence -> Persistence # | |
| type Rep Persistence Source # | |
Limit setting for the ConcUnlift strategy.
Constructors
| Limited !Int | Behavior dependent on the For For |
| Unlimited | Unlimited use of the unlifting function. |
Instances
| Generic Limit Source # | |
| Show Limit Source # | |
| Eq Limit Source # | |
| Ord Limit Source # | |
| type Rep Limit Source # | |
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)) | |
Unlifting functions
ephemeralConcLimitedUnlift Source #
Arguments
| :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r)) | |
| => Env es | |
| -> Int | Number of permitted uses of the unlift function. |
| -> ((forall r. effEs r -> IO r) -> IO a) | |
| -> IO a |
Concurrent unlift with limited uses that doesn't preserve the environment between calls to the unlifting function in threads other than its creator.
Since: 2.7.0.0
ephemeralConcUnlimitedUnlift :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r)) => Env es -> ((forall r. effEs r -> IO r) -> IO a) -> IO a Source #
Concurrent unlift with unlimited uses that doesn't preserve the environment between calls to the unlifting function in threads other than its creator.
Since: 2.7.0.0
Arguments
| :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r)) | |
| => Env es | |
| -> Bool | |
| -> Int | Number of threads that are allowed to use the unlift function. |
| -> ((forall r. effEs r -> IO r) -> IO a) | |
| -> IO a |
Concurrent unlift that preserves the environment between calls to the unlifting function within a particular thread.
persistentConcSingleUnlift :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r)) => Env es -> ((forall r. effEs r -> IO r) -> IO a) -> IO a Source #
Variant of persistentConcUnlift for a single other thread that doesn't
need ThreadEntries.
Since: 2.7.0.0
persistentConcUnlifts Source #
Arguments
| :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r), forall r. Coercible (effLocalEs r) (Env localEs -> IO r)) | |
| => Env es | |
| -> Env localEs | |
| -> Bool | |
| -> Int | Number of threads that are allowed to use the unlift function. |
| -> ((forall r. effEs r -> IO r) -> (forall r. effLocalEs r -> IO r) -> IO a) | |
| -> IO a |
Variant of persistentConcUnlift producing two unlifting functions that
share the effect storage in each thread.
Since: 2.7.0.0
persistentConcSingleUnlifts :: (HasCallStack, forall r. Coercible (effEs r) (Env es -> IO r), forall r. Coercible (effLocalEs r) (Env localEs -> IO r)) => Env es -> Env localEs -> ((forall r. effEs r -> IO r) -> (forall r. effLocalEs r -> IO r) -> IO a) -> IO a Source #
Variant of persistentConcUnlifts for a single other thread that doesn't
need ThreadEntries.
Since: 2.7.0.0