on master help sign in
Documentation
a9516acAndrzej Rybczak
effectful-core/src/Effectful/Error.hs+1−2
@@ -6,8 +6,7 @@ is __not__ intended to be a general mechanism for catching errors, that's what
6functions from the "Control.Monad.Catch" module are for.
6functions from the "Control.Monad.Catch" module are for.
7
7
8For example, if you want to catch an unchecked exception like
8For example, if you want to catch an unchecked exception like
9−'Control.Exception.SomeException' or 'Control.Exception.ErrorCall' you could use
9+'Control.Exception.ErrorCall' you could use 'Control.Monad.Catch.catch':
10−'Control.Monad.Catch.catch':
11
10
12>>> import qualified Control.Monad.Catch as E
11>>> import qualified Control.Monad.Catch as E
13>>> :{
12>>> :{
effectful-core/src/Effectful/Handler.hs+1−1
@@ -72,7 +72,7 @@ runHandler e m = unsafeEff $ \es0 -> do
72----------------------------------------
72----------------------------------------
73-- Sending operations
73-- Sending operations
74
74
75−-- | Send an operation of a given effect to its handler for execution.
75+-- | Send an operation of the given effect to its handler for execution.
76send :: (HasCallStack, e :> es) => e (Eff es) a -> Eff es a
76send :: (HasCallStack, e :> es) => e (Eff es) a -> Eff es a
77send op = unsafeEff $ \es -> do
77send op = unsafeEff $ \es -> do
78 Handler env handle <- getEnv es
78 Handler env handle <- getEnv es
effectful-core/src/Effectful/Internal/Env.hs+7−1
@@ -149,11 +149,14 @@ newForkId (ForkIdGen ref) = do
149----------------------------------------
149----------------------------------------
150-- Relinker
150-- Relinker
151
151
152+-- | A function for relinking 'Env' objects stored in the handlers when cloning
153+-- the environment.
152newtype Relinker :: (Effect -> Type) -> Effect -> Type where
154newtype Relinker :: (Effect -> Type) -> Effect -> Type where
153 Relinker
155 Relinker
154 :: ((forall es. Env es -> IO (Env es)) -> handler e -> IO (handler e))
156 :: ((forall es. Env es -> IO (Env es)) -> handler e -> IO (handler e))
155 -> Relinker handler e
157 -> Relinker handler e
156
158
159+-- | A dummy 'Relinker' that does nothing.
157noRelinker :: Relinker handler e
160noRelinker :: Relinker handler e
158noRelinker = Relinker $ \_ -> pure
161noRelinker = Relinker $ \_ -> pure
159
162
@@ -400,7 +403,10 @@ unsafeTailEnv len (Env fork gref _) = case fork of
400-- Data retrieval and update
403-- Data retrieval and update
401
404
402-- | Extract a specific data type from the environment.
405-- | Extract a specific data type from the environment.
406+getEnv
403−getEnv :: forall e es handler. e :> es => Env es -> IO (handler e)
407+ :: forall e es handler. e :> es
408+ => Env es
409+ -> IO (handler e)
404getEnv env = do
410getEnv env = do
405 (i, es) <- getLocation @e env
411 (i, es) <- getLocation @e env
406 fromAny <$> readSmallArray es i
412 fromAny <$> readSmallArray es i
effectful-core/src/Effectful/Internal/Monad.hs+1−1
@@ -383,7 +383,7 @@ unliftStrategy = do
383 IdE (IOE unlift) <- getEffect
383 IdE (IOE unlift) <- getEffect
384 pure unlift
384 pure unlift
385
385
386−-- | Locally override the 'UnliftStrategy' with a given value.
386+-- | Locally override the 'UnliftStrategy' with the given value.
387--
387--
388-- /Note:/ the strategy is reset to 'SeqUnlift' for new threads.
388-- /Note:/ the strategy is reset to 'SeqUnlift' for new threads.
389withUnliftStrategy :: IOE :> es => UnliftStrategy -> Eff es a -> Eff es a
389withUnliftStrategy :: IOE :> es => UnliftStrategy -> Eff es a -> Eff es a
effectful-core/src/Effectful/Reader.hs+23−6
@@ -15,18 +15,35 @@ import Effectful.Internal.Monad
15newtype Reader r :: Effect where
15newtype Reader r :: Effect where
16 Reader :: r -> Reader r m a
16 Reader :: r -> Reader r m a
17
17
18+-- | Run a 'Reader' effect with the given initial environment.
19+runReader
20+ :: r -- ^ An initial environment.
18−runReader :: r -> Eff (Reader r : es) a -> Eff es a
21+ -> Eff (Reader r : es) a
22+ -> Eff es a
19runReader r = evalEffect (IdE (Reader r))
23runReader r = evalEffect (IdE (Reader r))
20
24
25+-- | Fetch the value of the environment.
21ask :: Reader r :> es => Eff es r
26ask :: Reader r :> es => Eff es r
22ask = do
27ask = do
23 IdE (Reader r) <- getEffect
28 IdE (Reader r) <- getEffect
24 pure r
29 pure r
25
30
31+-- | Retrieve a function of the current environment.
32+--
33+-- @'asks' f ≡ f '<$>' 'ask'@
34+asks
26−asks :: Reader r :> es => (r -> a) -> Eff es a
35+ :: Reader r :> es
36+ => (r -> a) -- ^ The function to apply to the environment.
37+ -> Eff es a
27−asks f = do
38+asks f = f <$> ask
28− IdE (Reader r) <- getEffect
29− pure $ f r
30
39
40+-- | Execute a computation in a modified environment.
41+--
42+-- @'runReader' r ('local' f m) ≡ 'runReader' (f r) m@
43+--
44+local
31−local :: Reader r :> es => (r -> r) -> Eff es a -> Eff es a
45+ :: Reader r :> es
46+ => (r -> r) -- ^ The function to modify the environment.
47+ -> Eff es a
48+ -> Eff es a
32local f = localEffect $ \(IdE (Reader r)) -> IdE (Reader (f r))
49local f = localEffect $ \(IdE (Reader r)) -> IdE (Reader (f r))
effectful-core/src/Effectful/State/Dynamic.hs−11
@@ -1,13 +1,4 @@
1-- | The 'State' effect with dynamic dispatch.
1-- | The 'State' effect with dynamic dispatch.
2−--
3−-- It's not clear in which situation it's beneficial to use this instead of
4−-- "Effectful.State.Local" or "Effectful.State.Shared" as you either:
5−--
6−-- - Share state between threads and need the shared version.
7−--
8−-- - Don't share state between threads (or want it to be thread local) and are
9−-- free to use the faster, local version.
10−--
11module Effectful.State.Dynamic
2module Effectful.State.Dynamic
12 ( State(..)
3 ( State(..)
13
4
@@ -37,8 +28,6 @@ import qualified Effectful.State.Local as L
37import qualified Effectful.State.Shared as S
28import qualified Effectful.State.Shared as S
38
29
39-- | Provide access to a mutable state of type @s@.
30-- | Provide access to a mutable state of type @s@.
40−--
41−-- Whether the state is local or shared depends on the interpretation.
42data State s :: Effect where
31data State s :: Effect where
43 Get :: State s m s
32 Get :: State s m s
44 Put :: s -> State s m ()
33 Put :: s -> State s m ()
effectful-core/src/Effectful/State/Local.hs+52−9
@@ -28,40 +28,83 @@ import Effectful.Internal.Monad
28newtype State s :: Effect where
28newtype State s :: Effect where
29 State :: s -> State s m r
29 State :: s -> State s m r
30
30
31+-- | Run a 'State' effect with the given initial state and return the final
32+-- value along with the final state.
33+runState
34+ :: s -- ^ An initial state.
31−runState :: s -> Eff (State s : es) a -> Eff es (a, s)
35+ -> Eff (State s : es) a
36+ -> Eff es (a, s)
32runState s0 m = do
37runState s0 m = do
33 (a, IdE (State s)) <- runEffect (IdE (State s0)) m
38 (a, IdE (State s)) <- runEffect (IdE (State s0)) m
34 pure (a, s)
39 pure (a, s)
35
40
41+-- | Run a 'State' effect with the given initial state and return the final
42+-- value, discarding the final state.
43+evalState
44+ :: s -- ^ An initial state.
36−evalState :: s -> Eff (State s : es) a -> Eff es a
45+ -> Eff (State s : es) a
46+ -> Eff es a -- ^ A return value.
37evalState s = evalEffect (IdE (State s))
47evalState s = evalEffect (IdE (State s))
38
48
49+-- | Run a 'State' effect with the given initial state and return the final
50+-- state, discarding the final value.
51+execState
52+ :: s -- ^ An initial state.
39−execState :: s -> Eff (State s : es) a -> Eff es s
53+ -> Eff (State s : es) a
54+ -> Eff es s
40execState s0 m = do
55execState s0 m = do
41 IdE (State s) <- execEffect (IdE (State s0)) m
56 IdE (State s) <- execEffect (IdE (State s0)) m
42 pure s
57 pure s
43
58
59+-- | Fetch the current value of the state.
44get :: State s :> es => Eff es s
60get :: State s :> es => Eff es s
45get = do
61get = do
46 IdE (State s) <- getEffect
62 IdE (State s) <- getEffect
47 pure s
63 pure s
48
64
65+-- | Get a function of the current state.
66+--
67+-- @'gets' f ≡ f '<$>' 'get'@
68+gets
49−gets :: State s :> es => (s -> a) -> Eff es a
69+ :: State s :> es
70+ => (s -> a) -- ^ The function to apply to the state.
71+ -> Eff es a
50gets f = f <$> get
72gets f = f <$> get
51
73
74+-- | Set the current state to the given value.
52put :: State s :> es => s -> Eff es ()
75put :: State s :> es => s -> Eff es ()
53put s = putEffect (IdE (State s))
76put s = putEffect (IdE (State s))
54
77
78+-- | Apply the function to the current state and return a value.
79+state
55−state :: State s :> es => (s -> (a, s)) -> Eff es a
80+ :: State s :> es
81+ => (s -> (a, s)) -- ^ The function to modify the state.
82+ -> Eff es a
56state f = stateEffect $ \(IdE (State s0)) -> let (a, s) = f s0 in (a, IdE (State s))
83state f = stateEffect $ \(IdE (State s0)) -> let (a, s) = f s0 in (a, IdE (State s))
57
84
85+-- | Apply the function to the current state.
86+--
87+-- @'modify' f ≡ 'state' (\\s -> ((), f s))@
88+modify
58−modify :: State s :> es => (s -> s) -> Eff es ()
89+ :: State s :> es
90+ => (s -> s) -- ^ The function to modify the state.
91+ -> Eff es ()
59−modify f = state (\s -> ((), f s))
92+modify f = state $ \s -> ((), f s)
60
93
94+-- | Apply the monadic function to the current state and return a value.
95+stateM
61−stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
96+ :: State s :> es
97+ => (s -> Eff es (a, s)) -- ^ The function to modify the state.
98+ -> Eff es a
62stateM f = stateEffectM $ \(IdE (State s0)) -> do
99stateM f = stateEffectM $ \(IdE (State s0)) -> do
63 (a, s) <- f s0
100 (a, s) <- f s0
64 pure (a, IdE (State s))
101 pure (a, IdE (State s))
65
102
103+-- | Apply the monadic function to the current state.
104+--
105+-- @'modifyM' f ≡ 'stateM' (\\s -> ((), ) '<$>' f s)@
106+modifyM
66−modifyM :: State s :> es => (s -> Eff es s) -> Eff es ()
107+ :: State s :> es
108+ => (s -> Eff es s) -- ^ The monadic function to modify the state.
109+ -> Eff es ()
67modifyM f = stateM (\s -> ((), ) <$> f s)
110modifyM f = stateM (\s -> ((), ) <$> f s)
effectful-core/src/Effectful/Writer/Dynamic.hs+3−3
@@ -64,19 +64,19 @@ sharedWriter env = \case
64-- Operations
64-- Operations
65
65
66tell
66tell
67− :: (HasCallStack, Writer w :> es, Monoid w)
67+ :: (HasCallStack, Writer w :> es)
68 => w
68 => w
69 -> Eff es ()
69 -> Eff es ()
70tell = send . Tell
70tell = send . Tell
71
71
72listen
72listen
73− :: (HasCallStack, Writer w :> es, Monoid w)
73+ :: (HasCallStack, Writer w :> es)
74 => Eff es a
74 => Eff es a
75 -> Eff es (a, w)
75 -> Eff es (a, w)
76listen = send . Listen
76listen = send . Listen
77
77
78listens
78listens
79− :: (HasCallStack, Writer w :> es, Monoid w)
79+ :: (HasCallStack, Writer w :> es)
80 => (w -> b)
80 => (w -> b)
81 -> Eff es a
81 -> Eff es a
82 -> Eff es (a, b)
82 -> Eff es (a, b)
effectful-core/src/Effectful/Writer/Local.hs+34
@@ -1,4 +1,22 @@
1-- | The 'Writer' effect.
1-- | The 'Writer' effect.
2+--
3+-- Represented as a pure value underneath, therefore:
4+--
5+-- - thread local (if you need the state to be shared between threads, have a
6+-- look at "Effectful.Writer.Shared"),
7+--
8+-- - very fast.
9+--
10+-- /Warning:/ 'Writer'\'s state will be accumulated via __left-associated__ uses
11+-- of '<>', which makes it unsuitable for use with types for which such pattern
12+-- is inefficient. __This applies, in particular, to the standard list type__,
13+-- which makes the 'Writer' effect pretty niche.
14+--
15+-- /Note:/ while the 'Control.Monad.Trans.Writer.Strict.Writer' from the
16+-- @transformers@ package includes additional operations
17+-- 'Control.Monad.Trans.Writer.Strict.pass' and
18+-- 'Control.Monad.Trans.Writer.Strict.censor', they don't cooperate with runtime
19+-- exceptions very well, so they're deliberately omitted here.
2module Effectful.Writer.Local
20module Effectful.Writer.Local
3 ( Writer
21 ( Writer
4 , runWriter
22 , runWriter
@@ -19,19 +37,30 @@ import Effectful.Internal.Monad
19newtype Writer w :: Effect where
37newtype Writer w :: Effect where
20 Writer :: w -> Writer w m r
38 Writer :: w -> Writer w m r
21
39
40+-- | Run a 'Writer' effect and return the final value along with the final
41+-- output.
22runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
42runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
23runWriter m = do
43runWriter m = do
24 (a, IdE (Writer w)) <- runEffect (IdE (Writer mempty)) m
44 (a, IdE (Writer w)) <- runEffect (IdE (Writer mempty)) m
25 pure (a, w)
45 pure (a, w)
26
46
47+-- | Run a 'Writer' effect and return the final output, discarding the final
48+-- value.
27execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w
49execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w
28execWriter m = do
50execWriter m = do
29 IdE (Writer w) <- execEffect (IdE (Writer mempty)) m
51 IdE (Writer w) <- execEffect (IdE (Writer mempty)) m
30 pure w
52 pure w
31
53
54+-- | Append the given output to the overall output of the 'Writer'.
32tell :: (Writer w :> es, Monoid w) => w -> Eff es ()
55tell :: (Writer w :> es, Monoid w) => w -> Eff es ()
33tell w = stateEffect $ \(IdE (Writer w0)) -> ((), IdE (Writer (w0 <> w)))
56tell w = stateEffect $ \(IdE (Writer w0)) -> ((), IdE (Writer (w0 <> w)))
34
57
58+-- | Execute an action and append its output to the overall output of the
59+-- 'Writer'.
60+--
61+-- /Note:/ if a runtime exception is received while the action is executed, the
62+-- partial output of the action will still be appended to the overall output of
63+-- the 'Writer'.
35listen :: (Writer w :> es, Monoid w) => Eff es a -> Eff es (a, w)
64listen :: (Writer w :> es, Monoid w) => Eff es a -> Eff es (a, w)
36listen m = unsafeEff $ \es -> mask $ \restore -> do
65listen m = unsafeEff $ \es -> mask $ \restore -> do
37 w0 <- stateEnv es $ \(IdE (Writer w)) -> (w, IdE (Writer mempty))
66 w0 <- stateEnv es $ \(IdE (Writer w)) -> (w, IdE (Writer mempty))
@@ -42,6 +71,11 @@ listen m = unsafeEff $ \es -> mask $ \restore -> do
42 -- If an exception is thrown, restore w0 and keep parts of w1.
71 -- If an exception is thrown, restore w0 and keep parts of w1.
43 stateEnv es $ \(IdE (Writer w1)) -> (w1, IdE (Writer (w0 <> w1)))
72 stateEnv es $ \(IdE (Writer w1)) -> (w1, IdE (Writer (w0 <> w1)))
44
73
74+-- | Execute an action and append its output to the overall output of the
75+-- 'Writer', then return the final value along with a function of the recorded
76+-- output.
77+--
78+-- @'listens' f m ≡ 'Data.Bifunctor.second' f '<$>' 'listen' m@
45listens :: (Writer w :> es, Monoid w) => (w -> b) -> Eff es a -> Eff es (a, b)
79listens :: (Writer w :> es, Monoid w) => (w -> b) -> Eff es a -> Eff es (a, b)
46listens f m = do
80listens f m = do
47 (a, w) <- listen m
81 (a, w) <- listen m