on master help sign in
Documentation
commita9516ac93d550f87bec645731230c4fb2a05af8c authorAndrzej Rybczak <andrzej@rybczak.net> dateSat 31 Jul 2021, 15:52 (5 years ago) parent115aa0a
9 files changed+122−34
effectful-core/src/Effectful/Error.hs+1−2 effectful-core/src/Effectful/Handler.hs+1−1 effectful-core/src/Effectful/Internal/Env.hs+7−1 effectful-core/src/Effectful/Internal/Monad.hs+1−1 effectful-core/src/Effectful/Reader.hs+23−6 effectful-core/src/Effectful/State/Dynamic.hs−11 effectful-core/src/Effectful/State/Local.hs+52−9 effectful-core/src/Effectful/Writer/Dynamic.hs+3−3 effectful-core/src/Effectful/Writer/Local.hs+34
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
66functions from the "Control.Monad.Catch" module are for.
77
88For example, if you want to catch an unchecked exception like
9−'Control.Exception.SomeException' or 'Control.Exception.ErrorCall' you could use
10−'Control.Monad.Catch.catch':
9+'Control.Exception.ErrorCall' you could use 'Control.Monad.Catch.catch':
1110
1211>>> import qualified Control.Monad.Catch as E
1312>>> :{
effectful-core/src/Effectful/Handler.hs+1−1
@@ -72,7 +72,7 @@ runHandler e m = unsafeEff $ \es0 -> do
7272----------------------------------------
7373-- Sending operations
7474
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.
7676send :: (HasCallStack, e :> es) => e (Eff es) a -> Eff es a
7777send op = unsafeEff $ \es -> do
7878 Handler env handle <- getEnv es
effectful-core/src/Effectful/Internal/Env.hs+7−1
@@ -149,11 +149,14 @@ newForkId (ForkIdGen ref) = do
149149----------------------------------------
150150-- Relinker
151151
152+-- | A function for relinking 'Env' objects stored in the handlers when cloning
153+-- the environment.
152154newtype Relinker :: (Effect -> Type) -> Effect -> Type where
153155 Relinker
154156 :: ((forall es. Env es -> IO (Env es)) -> handler e -> IO (handler e))
155157 -> Relinker handler e
156158
159+-- | A dummy 'Relinker' that does nothing.
157160noRelinker :: Relinker handler e
158161noRelinker = Relinker $ \_ -> pure
159162
@@ -400,7 +403,10 @@ unsafeTailEnv len (Env fork gref _) = case fork of
400403-- Data retrieval and update
401404
402405-- | Extract a specific data type from the environment.
403−getEnv :: forall e es handler. e :> es => Env es -> IO (handler e)
406+getEnv
407+ :: forall e es handler. e :> es
408+ => Env es
409+ -> IO (handler e)
404410getEnv env = do
405411 (i, es) <- getLocation @e env
406412 fromAny <$> readSmallArray es i
effectful-core/src/Effectful/Internal/Monad.hs+1−1
@@ -383,7 +383,7 @@ unliftStrategy = do
383383 IdE (IOE unlift) <- getEffect
384384 pure unlift
385385
386−-- | Locally override the 'UnliftStrategy' with a given value.
386+-- | Locally override the 'UnliftStrategy' with the given value.
387387--
388388-- /Note:/ the strategy is reset to 'SeqUnlift' for new threads.
389389withUnliftStrategy :: 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
1515newtype Reader r :: Effect where
1616 Reader :: r -> Reader r m a
1717
18−runReader :: r -> Eff (Reader r : es) a -> Eff es a
18+-- | Run a 'Reader' effect with the given initial environment.
19+runReader
20+ :: r -- ^ An initial environment.
21+ -> Eff (Reader r : es) a
22+ -> Eff es a
1923runReader r = evalEffect (IdE (Reader r))
2024
25+-- | Fetch the value of the environment.
2126ask :: Reader r :> es => Eff es r
2227ask = do
2328 IdE (Reader r) <- getEffect
2429 pure r
2530
26−asks :: Reader r :> es => (r -> a) -> Eff es a
27−asks f = do
28− IdE (Reader r) <- getEffect
29− pure $ f r
31+-- | Retrieve a function of the current environment.
32+--
33+-- @'asks' f ≡ f '<$>' 'ask'@
34+asks
35+ :: Reader r :> es
36+ => (r -> a) -- ^ The function to apply to the environment.
37+ -> Eff es a
38+asks f = f <$> ask
3039
31−local :: Reader r :> es => (r -> r) -> Eff es a -> Eff es a
40+-- | Execute a computation in a modified environment.
41+--
42+-- @'runReader' r ('local' f m) ≡ 'runReader' (f r) m@
43+--
44+local
45+ :: Reader r :> es
46+ => (r -> r) -- ^ The function to modify the environment.
47+ -> Eff es a
48+ -> Eff es a
3249local f = localEffect $ \(IdE (Reader r)) -> IdE (Reader (f r))
effectful-core/src/Effectful/State/Dynamic.hs−11
@@ -1,13 +1,4 @@
11-- | 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−--
112module Effectful.State.Dynamic
123 ( State(..)
134
@@ -37,8 +28,6 @@ import qualified Effectful.State.Local as L
3728import qualified Effectful.State.Shared as S
3829
3930-- | Provide access to a mutable state of type @s@.
40−--
41−-- Whether the state is local or shared depends on the interpretation.
4231data State s :: Effect where
4332 Get :: State s m s
4433 Put :: s -> State s m ()
effectful-core/src/Effectful/State/Local.hs+52−9
@@ -28,40 +28,83 @@ import Effectful.Internal.Monad
2828newtype State s :: Effect where
2929 State :: s -> State s m r
3030
31−runState :: s -> Eff (State s : es) a -> Eff es (a, s)
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.
35+ -> Eff (State s : es) a
36+ -> Eff es (a, s)
3237runState s0 m = do
3338 (a, IdE (State s)) <- runEffect (IdE (State s0)) m
3439 pure (a, s)
3540
36−evalState :: s -> Eff (State s : es) a -> Eff es a
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.
45+ -> Eff (State s : es) a
46+ -> Eff es a -- ^ A return value.
3747evalState s = evalEffect (IdE (State s))
3848
39−execState :: s -> Eff (State s : es) a -> Eff es s
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.
53+ -> Eff (State s : es) a
54+ -> Eff es s
4055execState s0 m = do
4156 IdE (State s) <- execEffect (IdE (State s0)) m
4257 pure s
4358
59+-- | Fetch the current value of the state.
4460get :: State s :> es => Eff es s
4561get = do
4662 IdE (State s) <- getEffect
4763 pure s
4864
49−gets :: State s :> es => (s -> a) -> Eff es a
65+-- | Get a function of the current state.
66+--
67+-- @'gets' f ≡ f '<$>' 'get'@
68+gets
69+ :: State s :> es
70+ => (s -> a) -- ^ The function to apply to the state.
71+ -> Eff es a
5072gets f = f <$> get
5173
74+-- | Set the current state to the given value.
5275put :: State s :> es => s -> Eff es ()
5376put s = putEffect (IdE (State s))
5477
55−state :: State s :> es => (s -> (a, s)) -> Eff es a
78+-- | Apply the function to the current state and return a value.
79+state
80+ :: State s :> es
81+ => (s -> (a, s)) -- ^ The function to modify the state.
82+ -> Eff es a
5683state f = stateEffect $ \(IdE (State s0)) -> let (a, s) = f s0 in (a, IdE (State s))
5784
58−modify :: State s :> es => (s -> s) -> Eff es ()
59−modify f = state (\s -> ((), f s))
85+-- | Apply the function to the current state.
86+--
87+-- @'modify' f ≡ 'state' (\\s -> ((), f s))@
88+modify
89+ :: State s :> es
90+ => (s -> s) -- ^ The function to modify the state.
91+ -> Eff es ()
92+modify f = state $ \s -> ((), f s)
6093
61−stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
94+-- | Apply the monadic function to the current state and return a value.
95+stateM
96+ :: State s :> es
97+ => (s -> Eff es (a, s)) -- ^ The function to modify the state.
98+ -> Eff es a
6299stateM f = stateEffectM $ \(IdE (State s0)) -> do
63100 (a, s) <- f s0
64101 pure (a, IdE (State s))
65102
66−modifyM :: State s :> es => (s -> Eff es s) -> Eff es ()
103+-- | Apply the monadic function to the current state.
104+--
105+-- @'modifyM' f ≡ 'stateM' (\\s -> ((), ) '<$>' f s)@
106+modifyM
107+ :: State s :> es
108+ => (s -> Eff es s) -- ^ The monadic function to modify the state.
109+ -> Eff es ()
67110modifyM f = stateM (\s -> ((), ) <$> f s)
effectful-core/src/Effectful/Writer/Dynamic.hs+3−3
@@ -64,19 +64,19 @@ sharedWriter env = \case
6464-- Operations
6565
6666tell
67− :: (HasCallStack, Writer w :> es, Monoid w)
67+ :: (HasCallStack, Writer w :> es)
6868 => w
6969 -> Eff es ()
7070tell = send . Tell
7171
7272listen
73− :: (HasCallStack, Writer w :> es, Monoid w)
73+ :: (HasCallStack, Writer w :> es)
7474 => Eff es a
7575 -> Eff es (a, w)
7676listen = send . Listen
7777
7878listens
79− :: (HasCallStack, Writer w :> es, Monoid w)
79+ :: (HasCallStack, Writer w :> es)
8080 => (w -> b)
8181 -> Eff es a
8282 -> Eff es (a, b)
effectful-core/src/Effectful/Writer/Local.hs+34
@@ -1,4 +1,22 @@
11-- | 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.
220module Effectful.Writer.Local
321 ( Writer
422 , runWriter
@@ -19,19 +37,30 @@ import Effectful.Internal.Monad
1937newtype Writer w :: Effect where
2038 Writer :: w -> Writer w m r
2139
40+-- | Run a 'Writer' effect and return the final value along with the final
41+-- output.
2242runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
2343runWriter m = do
2444 (a, IdE (Writer w)) <- runEffect (IdE (Writer mempty)) m
2545 pure (a, w)
2646
47+-- | Run a 'Writer' effect and return the final output, discarding the final
48+-- value.
2749execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w
2850execWriter m = do
2951 IdE (Writer w) <- execEffect (IdE (Writer mempty)) m
3052 pure w
3153
54+-- | Append the given output to the overall output of the 'Writer'.
3255tell :: (Writer w :> es, Monoid w) => w -> Eff es ()
3356tell w = stateEffect $ \(IdE (Writer w0)) -> ((), IdE (Writer (w0 <> w)))
3457
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'.
3564listen :: (Writer w :> es, Monoid w) => Eff es a -> Eff es (a, w)
3665listen m = unsafeEff $ \es -> mask $ \restore -> do
3766 w0 <- stateEnv es $ \(IdE (Writer w)) -> (w, IdE (Writer mempty))
@@ -42,6 +71,11 @@ listen m = unsafeEff $ \es -> mask $ \restore -> do
4271 -- If an exception is thrown, restore w0 and keep parts of w1.
4372 stateEnv es $ \(IdE (Writer w1)) -> (w1, IdE (Writer (w0 <> w1)))
4473
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@
4579listens :: (Writer w :> es, Monoid w) => (w -> b) -> Eff es a -> Eff es (a, b)
4680listens f m = do
4781 (a, w) <- listen m