@@ -1,4 +1,22 @@
1-- | The 'Writer' effect.
1-- | The 'Writer' effect.
3+-- Represented as a pure value underneath, therefore:
5+-- - thread local (if you need the state to be shared between threads, have a
6+-- look at "Effectful.Writer.Shared"),
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.
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
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
40+-- | Run a 'Writer' effect and return the final value along with the final
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)
47+-- | Run a 'Writer' effect and return the final output, discarding the final
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
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)))
58+-- | Execute an action and append its output to the overall output of the
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
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)))
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
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