on master help sign in
Describe caveats of the Concurrent effect
4704c35Andrzej Rybczak
effectful-core/src/Effectful.hs+3−3
@@ -8,10 +8,10 @@ module Effectful
8
8
9 -- * Running the 'Eff' monad
9 -- * Running the 'Eff' monad
10
10
11− -- ** Pure operations
11+ -- ** Pure computations
12 , runPureEff
12 , runPureEff
13
13
14− -- ** Operations with side effects
14+ -- ** Computations with side effects
15 , IOE
15 , IOE
16 , runEff
16 , runEff
17
17
@@ -32,7 +32,7 @@ module Effectful
32 , interpret
32 , interpret
33 , reinterpret
33 , reinterpret
34
34
35− -- ** Handling local 'Eff' operations
35+ -- ** Handling local 'Eff' computations
36 , LocalEnv
36 , LocalEnv
37
37
38 -- *** Unlifts
38 -- *** Unlifts
effectful-core/src/Effectful/Dispatch/Dynamic.hs+12−12
@@ -7,7 +7,7 @@ module Effectful.Dispatch.Dynamic
7 , interpret
7 , interpret
8 , reinterpret
8 , reinterpret
9
9
10− -- ** Handling local 'Eff' operations
10+ -- ** Handling local 'Eff' computations
11 , LocalEnv
11 , LocalEnv
12
12
13 -- *** Unlifts
13 -- *** Unlifts
@@ -121,7 +121,7 @@ localUnliftIO (LocalEnv les) strategy k = case strategy of
121 SeqUnlift -> liftIO $ seqUnliftIO les k
121 SeqUnlift -> liftIO $ seqUnliftIO les k
122 ConcUnlift p l -> liftIO $ concUnliftIO les p l k
122 ConcUnlift p l -> liftIO $ concUnliftIO les p l k
123
123
124−-- | Utility for lifting 'Eff' operations of type
124+-- | Utility for lifting 'Eff' computations of type
125--
125--
126-- @'Eff' es a -> 'Eff' es b@
126-- @'Eff' es a -> 'Eff' es b@
127--
127--
@@ -129,7 +129,7 @@ localUnliftIO (LocalEnv les) strategy k = case strategy of
129--
129--
130-- @'Eff' localEs a -> 'Eff' localEs b@
130-- @'Eff' localEs a -> 'Eff' localEs b@
131--
131--
132−-- /Note:/ the operation must not run its argument in a separate thread,
132+-- /Note:/ the computation must not run its argument in a separate thread,
133-- attempting to do so will result in a runtime error.
133-- attempting to do so will result in a runtime error.
134withLiftMap
134withLiftMap
135 :: (HasCallStack, SuffixOf es handlerEs)
135 :: (HasCallStack, SuffixOf es handlerEs)
@@ -145,7 +145,7 @@ withLiftMap !_ k = unsafeEff $ \es -> do
145 seqUnliftIO localEs $ \unlift -> do
145 seqUnliftIO localEs $ \unlift -> do
146 (`unEff` es) . mapEff . unsafeEff_ $ unlift m
146 (`unEff` es) . mapEff . unsafeEff_ $ unlift m
147
147
148−-- | Utility for lifting 'IO' operations of type
148+-- | Utility for lifting 'IO' computations of type
149--
149--
150-- @'IO' a -> 'IO' b@
150-- @'IO' a -> 'IO' b@
151--
151--
@@ -153,11 +153,11 @@ withLiftMap !_ k = unsafeEff $ \es -> do
153--
153--
154-- @'Eff' localEs a -> 'Eff' localEs b@
154-- @'Eff' localEs a -> 'Eff' localEs b@
155--
155--
156−-- /Note:/ the operation must not run its argument in a separate thread,
156+-- /Note:/ the computation must not run its argument in a separate thread,
157-- attempting to do so will result in a runtime error.
157-- attempting to do so will result in a runtime error.
158--
158--
159-- Useful e.g. for lifting the unmasking function in
159-- Useful e.g. for lifting the unmasking function in
160−-- 'Control.Exception.mask'-like operations:
160+-- 'Control.Exception.mask'-like computations:
161--
161--
162-- >>> :{
162-- >>> :{
163-- data Fork :: Effect where
163-- data Fork :: Effect where
@@ -187,10 +187,10 @@ withLiftMapIO !_ k = k $ \mapIO m -> unsafeEff $ \es -> do
187
187
188-- | Create a local lifting and unlifting function with the given strategy.
188-- | Create a local lifting and unlifting function with the given strategy.
189--
189--
190−-- Useful for lifting complicated 'Eff' operations where the monadic action
190+-- Useful for lifting complicated 'Eff' computations where the monadic action
191-- shows in both positive (as a result) and negative (as an argument) position.
191-- shows in both positive (as a result) and negative (as an argument) position.
192--
192--
193−-- /Note:/ depending on the operation you're lifting 'localUnlift' along with
193+-- /Note:/ depending on the computation you're lifting 'localUnlift' along with
194-- 'withLiftMap' might be enough and is more efficient.
194-- 'withLiftMap' might be enough and is more efficient.
195localLiftUnlift
195localLiftUnlift
196 :: (HasCallStack, SuffixOf es handlerEs)
196 :: (HasCallStack, SuffixOf es handlerEs)
@@ -213,11 +213,11 @@ localLiftUnlift (LocalEnv les) strategy k = case strategy of
213-- | Create a local unlifting function with the given strategy along with an
213-- | Create a local unlifting function with the given strategy along with an
214-- unrestricted lifting function.
214-- unrestricted lifting function.
215--
215--
216−-- Useful for lifting complicated 'IO' operations where the monadic action shows
216+-- Useful for lifting complicated 'IO' computations where the monadic action
217−-- in both positive (as a result) and negative (as an argument) position.
217+-- shows in both positive (as a result) and negative (as an argument) position.
218--
218--
219−-- /Note:/ depending on the operation you're lifting 'localUnliftIO' along with
219+-- /Note:/ depending on the computation you're lifting 'localUnliftIO' along
220−-- 'withLiftMapIO' might be enough and is more efficient.
220+-- with 'withLiftMapIO' might be enough and is more efficient.
221localLiftUnliftIO
221localLiftUnliftIO
222 :: (HasCallStack, SuffixOf es handlerEs, IOE :> es)
222 :: (HasCallStack, SuffixOf es handlerEs, IOE :> es)
223 => LocalEnv localEs handlerEs
223 => LocalEnv localEs handlerEs
effectful-core/src/Effectful/Dispatch/Static.hs+6−6
@@ -54,7 +54,7 @@ import Effectful.Internal.Effect
54import Effectful.Internal.Env
54import Effectful.Internal.Env
55import Effectful.Internal.Monad
55import Effectful.Internal.Monad
56
56
57−-- | Utility for lifting 'IO' operations of type
57+-- | Utility for lifting 'IO' computations of type
58--
58--
59-- @'IO' a -> 'IO' b@
59-- @'IO' a -> 'IO' b@
60--
60--
@@ -65,10 +65,10 @@ import Effectful.Internal.Monad
65-- This function is __unsafe__ because:
65-- This function is __unsafe__ because:
66--
66--
67-- - It can be used to introduce arbitrary 'IO' actions into pure 'Eff'
67-- - It can be used to introduce arbitrary 'IO' actions into pure 'Eff'
68−-- operations.
68+-- computations.
69--
69--
70−-- - The 'IO' operation must not run its argument in a separate thread, but it's
70+-- - The 'IO' computation must not run its argument in a separate thread, but
71−-- not checked anywhere.
71+-- it's not checked anywhere.
72unsafeLiftMapIO :: (IO a -> IO b) -> Eff es a -> Eff es b
72unsafeLiftMapIO :: (IO a -> IO b) -> Eff es a -> Eff es b
73unsafeLiftMapIO f m = unsafeEff $ \es -> f (unEff m es)
73unsafeLiftMapIO f m = unsafeEff $ \es -> f (unEff m es)
74
74
@@ -77,9 +77,9 @@ unsafeLiftMapIO f m = unsafeEff $ \es -> f (unEff m es)
77-- This function is __unsafe__ because:
77-- This function is __unsafe__ because:
78--
78--
79-- - It can be used to introduce arbitrary 'IO' actions into pure 'Eff'
79-- - It can be used to introduce arbitrary 'IO' actions into pure 'Eff'
80−-- operations.
80+-- computations.
81--
81--
82−-- - Unlifted 'Eff' operations must not be run in a thread distinct from the
82+-- - Unlifted 'Eff' computations must not be run in a thread distinct from the
83-- caller of 'unsafeUnliftIO', but it's not checked anywhere.
83-- caller of 'unsafeUnliftIO', but it's not checked anywhere.
84unsafeUnliftIO :: ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
84unsafeUnliftIO :: ((forall r. Eff es r -> IO r) -> IO a) -> Eff es a
85unsafeUnliftIO k = unsafeEff $ \es -> k (`unEff` es)
85unsafeUnliftIO k = unsafeEff $ \es -> k (`unEff` es)
effectful-core/src/Effectful/Error.hs+4−4
@@ -25,12 +25,12 @@
25-- resources such as 'Control.Monad.Catch.finally' and
25-- resources such as 'Control.Monad.Catch.finally' and
26-- 'Control.Monad.Catch.bracket' work as expected:
26-- 'Control.Monad.Catch.bracket' work as expected:
27--
27--
28−-- >>> let display = liftIO . putStrLn
28+-- >>> let msg = liftIO . putStrLn
29-- >>> :{
29-- >>> :{
30-- runEff . fmap (either (Left . snd) Right) . runError @String $ do
30-- runEff . fmap (either (Left . snd) Right) . runError @String $ do
31−-- E.bracket_ (display "Beginning.")
31+-- E.bracket_ (msg "Beginning.")
32−-- (display "Cleaning up.")
32+-- (msg "Cleaning up.")
33−-- (display "Computing." >> throwError "oops" >> display "More.")
33+-- (msg "Computing." >> throwError "oops" >> msg "More.")
34-- :}
34-- :}
35-- Beginning.
35-- Beginning.
36-- Computing.
36-- Computing.
effectful-core/src/Effectful/Internal/Effect.hs+3−3
@@ -29,10 +29,10 @@ newtype IdA :: Effect -> Type where
29
29
30-- | A constraint that requires that a particular effect @e@ is a member of the
30-- | A constraint that requires that a particular effect @e@ is a member of the
31-- type-level list @es@. This is used to parameterize an 'Effectful.Monad.Eff'
31-- type-level list @es@. This is used to parameterize an 'Effectful.Monad.Eff'
32−-- operation over an arbitrary list of effects, so long as @e@ is /somewhere/ in
32+-- computation over an arbitrary list of effects, so long as @e@ is /somewhere/
33−-- the list.
33+-- in the list.
34--
34--
35−-- For example, an operation that only needs access to a mutable value of type
35+-- For example, a computation that only needs access to a mutable value of type
36-- 'Integer' would likely use the following type:
36-- 'Integer' would likely use the following type:
37--
37--
38-- @
38-- @
effectful-core/src/Effectful/Internal/Monad.hs+17−17
@@ -84,12 +84,12 @@ import Effectful.Internal.Unlift
84
84
85type role Eff nominal representational
85type role Eff nominal representational
86
86
87−-- | The 'Eff' monad provides the implementation of an operation that performs
87+-- | The 'Eff' monad provides the implementation of a computation that performs
88-- an arbitrary set of effects. In @'Eff' es a@, @es@ is a type-level list that
88-- an arbitrary set of effects. In @'Eff' es a@, @es@ is a type-level list that
89−-- contains all the effects that the operation may perform. For example, an
89+-- contains all the effects that the computation may perform. For example, a
90−-- operation that produces an 'Integer' by consuming a 'String' from the global
90+-- computation that produces an 'Integer' by consuming a 'String' from the
91−-- environment and acting upon a single mutable value of type 'Bool' would have
91+-- global environment and acting upon a single mutable value of type 'Bool'
92−-- the following type:
92+-- would have the following type:
93--
93--
94-- @
94-- @
95-- 'Eff' '['Effectful.Reader.Reader' 'String', 'Effectful.State.State' 'Bool'] 'Integer'
95-- 'Eff' '['Effectful.Reader.Reader' 'String', 'Effectful.State.State' 'Bool'] 'Integer'
@@ -97,7 +97,7 @@ type role Eff nominal representational
97--
97--
98-- Normally, a concrete list of effects is not used to parameterize 'Eff'.
98-- Normally, a concrete list of effects is not used to parameterize 'Eff'.
99-- Instead, the '(:>)' type class is used to express constraints on the list of
99-- Instead, the '(:>)' type class is used to express constraints on the list of
100−-- effects without coupling an operation to a concrete list of effects. For
100+-- effects without coupling a computation to a concrete list of effects. For
101-- example, the above example would more commonly be expressed with the
101-- example, the above example would more commonly be expressed with the
102-- following type:
102-- following type:
103--
103--
@@ -105,15 +105,15 @@ type role Eff nominal representational
105-- ('Effectful.Reader.Reader' 'String' ':>' es, 'Effectful.State.State' 'Bool' ':>' es) => 'Eff' es 'Integer'
105-- ('Effectful.Reader.Reader' 'String' ':>' es, 'Effectful.State.State' 'Bool' ':>' es) => 'Eff' es 'Integer'
106-- @
106-- @
107--
107--
108−-- This abstraction allows the operation to be used in functions that may
108+-- This abstraction allows the computation to be used in functions that may
109-- perform other effects, and it also allows the effects to be handled in any
109-- perform other effects, and it also allows the effects to be handled in any
110-- order.
110-- order.
111newtype Eff (es :: [Effect]) a = Eff (Env es -> IO a)
111newtype Eff (es :: [Effect]) a = Eff (Env es -> IO a)
112 deriving (Monoid, Semigroup)
112 deriving (Monoid, Semigroup)
113
113
114−-- | Run a pure 'Eff' operation.
114+-- | Run a pure 'Eff' computation.
115--
115--
116−-- For running operations with side effects see 'runEff'.
116+-- For running computations with side effects see 'runEff'.
117runPureEff :: Eff '[] a -> a
117runPureEff :: Eff '[] a -> a
118runPureEff (Eff m) =
118runPureEff (Eff m) =
119 -- unsafePerformIO is safe here since IOE was not on the stack, so no IO with
119 -- unsafePerformIO is safe here since IOE was not on the stack, so no IO with
@@ -134,14 +134,14 @@ unEff (Eff m) = m
134-- | Access the underlying 'IO' monad along with the environment.
134-- | Access the underlying 'IO' monad along with the environment.
135--
135--
136-- This function is __unsafe__ because it can be used to introduce arbitrary
136-- This function is __unsafe__ because it can be used to introduce arbitrary
137−-- 'IO' actions into pure 'Eff' operations.
137+-- 'IO' actions into pure 'Eff' computations.
138unsafeEff :: (Env es -> IO a) -> Eff es a
138unsafeEff :: (Env es -> IO a) -> Eff es a
139unsafeEff m = Eff (oneShot m)
139unsafeEff m = Eff (oneShot m)
140
140
141-- | Access the underlying 'IO' monad.
141-- | Access the underlying 'IO' monad.
142--
142--
143-- This function is __unsafe__ because it can be used to introduce arbitrary
143-- This function is __unsafe__ because it can be used to introduce arbitrary
144−-- 'IO' actions into pure 'Eff' operations.
144+-- 'IO' actions into pure 'Eff' computations.
145unsafeEff_ :: IO a -> Eff es a
145unsafeEff_ :: IO a -> Eff es a
146unsafeEff_ m = unsafeEff $ \_ -> m
146unsafeEff_ m = unsafeEff $ \_ -> m
147
147
@@ -264,13 +264,13 @@ instance Fail :> es => MonadFail (Eff es) where
264----------------------------------------
264----------------------------------------
265-- IO
265-- IO
266
266
267−-- | Run arbitrary 'IO' operations via 'MonadIO' or 'MonadUnliftIO'.
267+-- | Run arbitrary 'IO' computations via 'MonadIO' or 'MonadUnliftIO'.
268newtype IOE :: Effect where
268newtype IOE :: Effect where
269 IOE :: UnliftStrategy -> IOE m r
269 IOE :: UnliftStrategy -> IOE m r
270
270
271−-- | Run an 'Eff' operation with side effects.
271+-- | Run an 'Eff' computation with side effects.
272--
272--
273−-- For running pure operations see 'runPureEff'.
273+-- For running pure computations see 'runPureEff'.
274runEff :: Eff '[IOE] a -> IO a
274runEff :: Eff '[IOE] a -> IO a
275runEff m = unEff (evalEffect (IdA (IOE SeqUnlift)) m) =<< emptyEnv
275runEff m = unEff (evalEffect (IdA (IOE SeqUnlift)) m) =<< emptyEnv
276
276
@@ -300,7 +300,7 @@ instance IOE :> es => MonadBaseControl IO (Eff es) where
300data Prim :: Effect where
300data Prim :: Effect where
301 Prim :: Prim m r
301 Prim :: Prim m r
302
302
303−-- | Run an 'Eff' operation with primitive state-transformer actions.
303+-- | Run an 'Eff' computation with primitive state-transformer actions.
304runPrim :: IOE :> es => Eff (Prim : es) a -> Eff es a
304runPrim :: IOE :> es => Eff (Prim : es) a -> Eff es a
305runPrim = evalEffect (IdA Prim)
305runPrim = evalEffect (IdA Prim)
306
306
@@ -325,8 +325,8 @@ newtype LocalEnv (localEs :: [Effect]) (handlerEs :: [Effect]) = LocalEnv (Env l
325type EffectHandler e es
325type EffectHandler e es
326 = forall a localEs. HasCallStack
326 = forall a localEs. HasCallStack
327 => LocalEnv localEs es
327 => LocalEnv localEs es
328− -- ^ Capture of the local environment for handling local 'Eff' operations when
328+ -- ^ Capture of the local environment for handling local 'Eff' computations
329− -- @e@ is a higher order effect.
329+ -- when @e@ is a higher order effect.
330 -> e (Eff localEs) a
330 -> e (Eff localEs) a
331 -- ^ The effect performed in the local environment.
331 -- ^ The effect performed in the local environment.
332 -> Eff es a
332 -> Eff es a
effectful-core/src/Effectful/Internal/Unlift.hs+2−2
@@ -35,7 +35,7 @@ import Effectful.Internal.Utils
35----------------------------------------
35----------------------------------------
36-- Unlift strategies
36-- Unlift strategies
37
37
38−-- | The strategy to use when unlifting 'Eff' operations via 'withRunInIO',
38+-- | The strategy to use when unlifting 'Eff' computations via 'withRunInIO',
39-- 'Effectful.Monad.withEffToIO' or the 'Effectful.Dispatch.Dynamic.localUnlift'
39-- 'Effectful.Monad.withEffToIO' or the 'Effectful.Dispatch.Dynamic.localUnlift'
40-- family.
40-- family.
41data UnliftStrategy
41data UnliftStrategy
@@ -105,7 +105,7 @@ seqUnlift k es unEff = do
105 if tid `eqThreadId` tid0
105 if tid `eqThreadId` tid0
106 then unEff m es
106 then unEff m es
107 else error
107 else error
108− $ "If you want to use the unlifting function to run Eff operations "
108+ $ "If you want to use the unlifting function to run Eff computations "
109 ++ "in multiple threads, have a look at UnliftStrategy (ConcUnlift)."
109 ++ "in multiple threads, have a look at UnliftStrategy (ConcUnlift)."
110
110
111-- | Concurrent unlift for various strategies and limits.
111-- | Concurrent unlift for various strategies and limits.
effectful-core/src/Effectful/State/Shared.hs+3−3
@@ -1,7 +1,7 @@
1-- | Support for access to a mutable value of a particular type, which is:
1-- | Support for access to a mutable value of a particular type, which is:
2--
2--
3−-- - shareable between multiple threads (if you want each thead to manage their
3+-- - shared between multiple threads (if you want each thead to manage its own
4−-- own version of the value, see "Effectful.State.Local").
4+-- version of the value, see "Effectful.State.Local").
5--
5--
6-- - slower than "Effectful.State.Local".
6-- - slower than "Effectful.State.Local".
7--
7--
@@ -24,7 +24,7 @@ import Control.Concurrent.MVar
24import Effectful.Dispatch.Static
24import Effectful.Dispatch.Static
25import Effectful.Monad
25import Effectful.Monad
26
26
27−-- | Provide access to a strict (WHNF), shareable, mutable value of type @s@.
27+-- | Provide access to a strict (WHNF), shared, mutable value of type @s@.
28newtype State s :: Effect where
28newtype State s :: Effect where
29 State :: MVar s -> State s m r
29 State :: MVar s -> State s m r
30
30
effectful-core/src/Effectful/Writer/Shared.hs+3−3
@@ -1,7 +1,7 @@
1-- | Support for access to a write only value of a particular type, which is:
1-- | Support for access to a write only value of a particular type, which is:
2--
2--
3−-- - shareable between multiple threads (if you want each thead to manage their
3+-- - shared between multiple threads (if you want each thead to manage its own
4−-- own version of the value, see "Effectful.Writer.Local").
4+-- version of the value, see "Effectful.Writer.Local").
5--
5--
6-- - slower than "Effectful.Writer.Local".
6-- - slower than "Effectful.Writer.Local".
7--
7--
@@ -30,7 +30,7 @@ import Control.Exception
30import Effectful.Dispatch.Static
30import Effectful.Dispatch.Static
31import Effectful.Monad
31import Effectful.Monad
32
32
33−-- | Provide access to a strict (WHNF), shareable, write only value of type @w@.
33+-- | Provide access to a strict (WHNF), shared, write only value of type @w@.
34newtype Writer w :: Effect where
34newtype Writer w :: Effect where
35 Writer :: MVar w -> Writer w m r
35 Writer :: MVar w -> Writer w m r
36
36
effectful/src/Effectful/Concurrent.hs+15−2
@@ -1,6 +1,19 @@
1+-- | Lifted version of "Control.Concurrent".
2+--
3+-- For functions that spawn threads, the order of preference for their usage is
4+-- recommended as follows:
5+--
6+-- 1) High level functions from "Effectful.Concurrent.Async" such as
7+-- 'Effectful.Concurrent.Async.withAsync',
8+-- 'Effectful.Concurrent.Async.concurrently' or
9+-- 'Effectful.Concurrent.Async.mapConcurrently'.
10+--
11+-- 2) Low level functions from "Effectful.Concurrent.Async" such as
12+-- 'Effectful.Concurrent.Async.async'.
13+--
14+-- 3) Low level functions from "Effectful.Concurrent" such as 'forkIO'.
1module Effectful.Concurrent
15module Effectful.Concurrent
2− ( -- * The effect
3− Concurrent
16+ ( Concurrent
4 , runConcurrent
17 , runConcurrent
5
18
6 -- * Basic concurrency operations
19 -- * Basic concurrency operations
effectful/src/Effectful/Concurrent/Async.hs+2−2
@@ -1,7 +1,7 @@
1{-# LANGUAGE UndecidableInstances #-}
1{-# LANGUAGE UndecidableInstances #-}
2+-- | Lifted version of "Control.Concurrent.Async".
2module Effectful.Concurrent.Async
3module Effectful.Concurrent.Async
3− ( -- * The effect
4− Concurrent
4+ ( Concurrent
5 , runConcurrent
5 , runConcurrent
6
6
7 -- * Asynchronous actions
7 -- * Asynchronous actions
effectful/src/Effectful/Concurrent/Chan.hs+1−2
@@ -1,7 +1,6 @@
1-- | Lifted version of "Control.Concurrent.Chan".
1-- | Lifted version of "Control.Concurrent.Chan".
2module Effectful.Concurrent.Chan
2module Effectful.Concurrent.Chan
3− ( -- * The effect
4− Concurrent
3+ ( Concurrent
5 , runConcurrent
4 , runConcurrent
6
5
7 -- * Chan
6 -- * Chan
effectful/src/Effectful/Concurrent/Effect.hs+63−4
@@ -6,15 +6,74 @@ module Effectful.Concurrent.Effect
6import Effectful.Dispatch.Static
6import Effectful.Dispatch.Static
7import Effectful.Monad
7import Effectful.Monad
8
8
9−-- | Run 'Eff' operations asynchronously via the @async@ library.
9+-- | Provide the ability to run 'Eff' computations concurrently in multiple
10+-- threads and communicate between them.
10--
11--
11−-- /Note:/ thread local state changes in 'Eff' operations run asynchronously
12+-- /Warning:/ unless you stick to high level functions from the
12−-- will not affect the parent thread.
13+-- 'Effectful.Concurrent.Async.withAsync' family, the 'Concurrent' effect makes
14+-- it possible to escape the scope of any scoped effect operation. Consider the
15+-- following:
16+--
17+-- >>> import qualified Effectful.Reader as R
18+-- >>> let printAsk msg = liftIO . putStrLn . (msg ++) . (": " ++) =<< R.ask
19+-- >>> :{
20+-- runEff . R.runReader "GLOBAL" . runConcurrent $ do
21+-- a <- R.local (const "LOCAL") $ do
22+-- a <- async $ do
23+-- printAsk "child (first)"
24+-- threadDelay 20000
25+-- printAsk "child (second)"
26+-- threadDelay 10000
27+-- printAsk "parent (inside)"
28+-- pure a
29+-- printAsk "parent (outside)"
30+-- wait a
31+-- :}
32+-- child (first): LOCAL
33+-- parent (inside): LOCAL
34+-- parent (outside): GLOBAL
35+-- child (second): LOCAL
36+--
37+-- Note that the asynchronous computation doesn't respect the scope of
38+-- 'Effectful.Reader.local', i.e. the child thread still behaves like it's
39+-- inside the 'Effectful.Reader.local' block, even though the parent thread
40+-- already got out of it.
41+--
42+-- This is because the value provided by the 'Effectful.Reader.Reader' effect is
43+-- thread local, i.e. each thread manages its own version of it. For the
44+-- 'Effectful.Reader.Reader' it is the only reasonable behavior, it wouldn't be
45+-- very useful if its "read only" value was affected by calls to
46+-- 'Effectful.Reader.local' from its parent or child threads.
47+--
48+-- However, the cut isn't so clear if it comes to effects that provide access to
49+-- a mutable state. That's why the @State@ and the @Writer@ effects come in two
50+-- flavors, thread local and shared:
51+--
52+-- >>> import qualified Effectful.State.Local as SL
53+-- >>> :{
54+-- runEff . SL.execState "Hi" . runConcurrent $ do
55+-- replicateConcurrently_ 3 $ SL.modify (++ "!")
56+-- :}
57+-- "Hi"
58+--
59+-- >>> import qualified Effectful.State.Shared as SS
60+-- >>> :{
61+-- runEff . SS.execState "Hi" . runConcurrent $ do
62+-- replicateConcurrently_ 3 $ SS.modify (++ "!")
63+-- :}
64+-- "Hi!!!"
65+--
66+-- In the first example state modification made concurrently are not reflected
67+-- in the parent thread because the value is thread local, but in the second
68+-- example they are, because the value is shared.
13--
69--
14−-- /TODO:/ write about 'Concurrent' not respecting scoped operations.
15data Concurrent :: Effect where
70data Concurrent :: Effect where
16 Concurrent :: Concurrent m r
71 Concurrent :: Concurrent m r
17
72
18-- | Run the 'Concurrent' effect.
73-- | Run the 'Concurrent' effect.
19runConcurrent :: IOE :> es => Eff (Concurrent : es) a -> Eff es a
74runConcurrent :: IOE :> es => Eff (Concurrent : es) a -> Eff es a
20runConcurrent = evalEffect (IdA Concurrent)
75runConcurrent = evalEffect (IdA Concurrent)
76+
77+-- $setup
78+-- >>> import Effectful.Concurrent
79+-- >>> import Effectful.Concurrent.Async
effectful/src/Effectful/Concurrent/MVar.hs+1−2
@@ -1,7 +1,6 @@
1-- | Lifted version of "Control.Concurrent.MVar".
1-- | Lifted version of "Control.Concurrent.MVar".
2module Effectful.Concurrent.MVar
2module Effectful.Concurrent.MVar
3− ( -- * The effect
4− Concurrent
3+ ( Concurrent
5 , runConcurrent
4 , runConcurrent
6
5
7 -- * MVar
6 -- * MVar
effectful/src/Effectful/Concurrent/QSem.hs+2−3
@@ -1,7 +1,6 @@
1−-- | Lifted "Control.Concurrent.QSem".
1+-- | Lifted version of "Control.Concurrent.QSem".
2module Effectful.Concurrent.QSem
2module Effectful.Concurrent.QSem
3− ( -- * The effect
4− Concurrent
3+ ( Concurrent
5 , runConcurrent
4 , runConcurrent
6
5
7 -- * QSem
6 -- * QSem
effectful/src/Effectful/Concurrent/QSemN.hs+2−3
@@ -1,7 +1,6 @@
1−-- | Lifted "Control.Concurrent.QSemN".
1+-- | Lifted version of "Control.Concurrent.QSemN".
2module Effectful.Concurrent.QSemN
2module Effectful.Concurrent.QSemN
3− ( -- * The effect
4− Concurrent
3+ ( Concurrent
5 , runConcurrent
4 , runConcurrent
6
5
7 -- * QSemN
6 -- * QSemN
effectful/src/Effectful/Concurrent/STM.hs+1−2
@@ -1,7 +1,6 @@
1-- | Lifted version of "Control.Concurrent.STM".
1-- | Lifted version of "Control.Concurrent.STM".
2module Effectful.Concurrent.STM
2module Effectful.Concurrent.STM
3− ( -- * The effect
4− Concurrent
3+ ( Concurrent
5 , runConcurrent
4 , runConcurrent
6
5
7 -- * Core
6 -- * Core
effectful/src/Effectful/Environment.hs+1−2
@@ -1,6 +1,5 @@
1module Effectful.Environment
1module Effectful.Environment
2− ( -- * The effect
3− Environment
2+ ( Environment
4 , runEnvironment
3 , runEnvironment
5
4
6 -- * Querying the environment
5 -- * Querying the environment
effectful/src/Effectful/FileSystem.hs+88−89
@@ -1,93 +1,92 @@
1module Effectful.FileSystem
1module Effectful.FileSystem
2− ( -- * The effect
3− FileSystem
2+ ( FileSystem
4− , runFileSystem
3+ , runFileSystem
5−
4+
6− -- * Actions on directories
5+ -- * Actions on directories
7− , createDirectory
6+ , createDirectory
8− , createDirectoryIfMissing
7+ , createDirectoryIfMissing
9− , removeDirectory
8+ , removeDirectory
10− , removeDirectoryRecursive
9+ , removeDirectoryRecursive
11− , removePathForcibly
10+ , removePathForcibly
12− , renameDirectory
11+ , renameDirectory
13− , listDirectory
12+ , listDirectory
14− , getDirectoryContents
13+ , getDirectoryContents
15−
14+
16− -- ** Current working directory
15+ -- ** Current working directory
17− , getCurrentDirectory
16+ , getCurrentDirectory
18− , setCurrentDirectory
17+ , setCurrentDirectory
19− , withCurrentDirectory
18+ , withCurrentDirectory
20−
19+
21− -- * Pre-defined directories
20+ -- * Pre-defined directories
22− , getHomeDirectory
21+ , getHomeDirectory
23− , getXdgDirectory
22+ , getXdgDirectory
24− , getXdgDirectoryList
23+ , getXdgDirectoryList
25− , getAppUserDataDirectory
24+ , getAppUserDataDirectory
26− , getUserDocumentsDirectory
25+ , getUserDocumentsDirectory
27− , getTemporaryDirectory
26+ , getTemporaryDirectory
28−
27+
29− -- * Actions on files
28+ -- * Actions on files
30− , removeFile
29+ , removeFile
31− , renameFile
30+ , renameFile
32− , renamePath
31+ , renamePath
33− , copyFile
32+ , copyFile
34− , copyFileWithMetadata
33+ , copyFileWithMetadata
35− , getFileSize
34+ , getFileSize
36− , canonicalizePath
35+ , canonicalizePath
37− , makeAbsolute
36+ , makeAbsolute
38− , makeRelativeToCurrentDirectory
37+ , makeRelativeToCurrentDirectory
39−
38+
40− -- * Existence tests
39+ -- * Existence tests
41− , doesPathExist
40+ , doesPathExist
42− , doesFileExist
41+ , doesFileExist
43− , doesDirectoryExist
42+ , doesDirectoryExist
44− , findExecutable
43+ , findExecutable
45− , findExecutables
44+ , findExecutables
46− , findExecutablesInDirectories
45+ , findExecutablesInDirectories
47− , findFile
46+ , findFile
48− , findFiles
47+ , findFiles
49− , findFileWith
48+ , findFileWith
50− , findFilesWith
49+ , findFilesWith
51−
50+
52− -- * Symbolic links
51+ -- * Symbolic links
53− , createFileLink
52+ , createFileLink
54− , createDirectoryLink
53+ , createDirectoryLink
55− , removeDirectoryLink
54+ , removeDirectoryLink
56− , pathIsSymbolicLink
55+ , pathIsSymbolicLink
57− , getSymbolicLinkTarget
56+ , getSymbolicLinkTarget
58−
57+
59− -- * Permissions
58+ -- * Permissions
60− , getPermissions
59+ , getPermissions
61− , setPermissions
60+ , setPermissions
62− , copyPermissions
61+ , copyPermissions
63−
62+
64− -- * Timestamps
63+ -- * Timestamps
65− , getAccessTime
64+ , getAccessTime
66− , getModificationTime
65+ , getModificationTime
67− , setAccessTime
66+ , setAccessTime
68− , setModificationTime
67+ , setModificationTime
69−
68+
70− -- * Re-exports
69+ -- * Re-exports
71−
70+
72− -- ** Pre-defined directories
71+ -- ** Pre-defined directories
73− , D.XdgDirectory(..)
72+ , D.XdgDirectory(..)
74− , D.XdgDirectoryList(..)
73+ , D.XdgDirectoryList(..)
75−
74+
76− -- ** Existence tests
75+ -- ** Existence tests
77− , D.exeExtension
76+ , D.exeExtension
78−
77+
79− -- ** Permissions
78+ -- ** Permissions
80− , D.Permissions
79+ , D.Permissions
81− , D.emptyPermissions
80+ , D.emptyPermissions
82− , D.readable
81+ , D.readable
83− , D.writable
82+ , D.writable
84− , D.executable
83+ , D.executable
85− , D.searchable
84+ , D.searchable
86− , D.setOwnerReadable
85+ , D.setOwnerReadable
87− , D.setOwnerWritable
86+ , D.setOwnerWritable
88− , D.setOwnerExecutable
87+ , D.setOwnerExecutable
89− , D.setOwnerSearchable
88+ , D.setOwnerSearchable
90− ) where
89+ ) where
91
90
92import Data.Time (UTCTime)
91import Data.Time (UTCTime)
93import qualified System.Directory as D
92import qualified System.Directory as D
effectful/src/Effectful/FileSystem/IO.hs+1−2
@@ -1,7 +1,6 @@
1-- | Lifted version of "System.IO".
1-- | Lifted version of "System.IO".
2module Effectful.FileSystem.IO
2module Effectful.FileSystem.IO
3− ( -- * The effect
4− FileSystem
3+ ( FileSystem
5 , runFileSystem
4 , runFileSystem
6
5
7 -- * IO
6 -- * IO
effectful/src/Effectful/Process.hs+1−2
@@ -1,8 +1,7 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2-- | Lifted version of "System.Process".
2-- | Lifted version of "System.Process".
3module Effectful.Process
3module Effectful.Process
4− ( -- * The effect
5− Process
4+ ( Process
6 , runProcess
5 , runProcess
7
6
8 -- * Running sub-processes
7 -- * Running sub-processes