-- | The dynamically dispatched variant of the 'Writer' effect.
--
-- /Note:/ unless you plan to change interpretations at runtime, it's
-- recommended to use one of the statically dispatched variants,
-- i.e. "Effectful.Writer.Static.Local" or "Effectful.Writer.Static.Shared".
module Effectful.Writer.Dynamic
  ( -- * Effect
    Writer(..)

    -- ** Handlers

    -- *** Local
  , runWriterLocal
  , execWriterLocal

    -- *** Shared
  , runWriterShared
  , execWriterShared

    -- * Operations
  , tell
  , listen
  , listens
  ) where

import Effectful
import Effectful.Dispatch.Dynamic
import qualified Effectful.Writer.Static.Local as L
import qualified Effectful.Writer.Static.Shared as S

-- | Provide access to a write only value of type @w@.
data Writer w :: Effect where
  Tell   :: w   -> Writer w m ()
  Listen :: m a -> Writer w m (a, w)

type instance DispatchOf (Writer w) = Dynamic

----------------------------------------
-- Local

-- | Run the 'Writer' effect and return the final value along with the final
-- output (via "Effectful.Writer.Static.Local").
runWriterLocal :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
runWriterLocal :: Eff (Writer w : es) a -> Eff es (a, w)
runWriterLocal = (Eff (Writer w : es) a -> Eff es (a, w))
-> EffectHandler (Writer w) (Writer w : es)
-> Eff (Writer w : es) a
-> Eff es (a, w)
forall (e :: (Type -> Type) -> Type -> Type)
       (handlerEs :: [(Type -> Type) -> Type -> Type]) a
       (es :: [(Type -> Type) -> Type -> Type]) b.
(DispatchOf e ~ 'Dynamic) =>
(Eff handlerEs a -> Eff es b)
-> EffectHandler e handlerEs -> Eff (e : es) a -> Eff es b
reinterpret Eff (Writer w : es) a -> Eff es (a, w)
forall w (es :: [(Type -> Type) -> Type -> Type]) a.
Monoid w =>
Eff (Writer w : es) a -> Eff es (a, w)
L.runWriter EffectHandler (Writer w) (Writer w : es)
forall w (es :: [(Type -> Type) -> Type -> Type])
       (localEs :: [(Type -> Type) -> Type -> Type]) a.
(Writer w :> es, Monoid w) =>
LocalEnv localEs es -> Writer w (Eff localEs) a -> Eff es a
localWriter

-- | Run a 'Writer' effect and return the final output, discarding the final
-- value (via "Effectful.Writer.Static.Local").
execWriterLocal :: Monoid w => Eff (Writer w : es) a -> Eff es w
execWriterLocal :: Eff (Writer w : es) a -> Eff es w
execWriterLocal = (Eff (Writer w : es) a -> Eff es w)
-> EffectHandler (Writer w) (Writer w : es)
-> Eff (Writer w : es) a
-> Eff es w
forall (e :: (Type -> Type) -> Type -> Type)
       (handlerEs :: [(Type -> Type) -> Type -> Type]) a
       (es :: [(Type -> Type) -> Type -> Type]) b.
(DispatchOf e ~ 'Dynamic) =>
(Eff handlerEs a -> Eff es b)
-> EffectHandler e handlerEs -> Eff (e : es) a -> Eff es b
reinterpret Eff (Writer w : es) a -> Eff es w
forall w (es :: [(Type -> Type) -> Type -> Type]) a.
Monoid w =>
Eff (Writer w : es) a -> Eff es w
L.execWriter EffectHandler (Writer w) (Writer w : es)
forall w (es :: [(Type -> Type) -> Type -> Type])
       (localEs :: [(Type -> Type) -> Type -> Type]) a.
(Writer w :> es, Monoid w) =>
LocalEnv localEs es -> Writer w (Eff localEs) a -> Eff es a
localWriter

localWriter
  :: (L.Writer w :> es, Monoid w)
  => LocalEnv localEs es
  -> Writer w (Eff localEs) a
  -> Eff es a
localWriter :: LocalEnv localEs es -> Writer w (Eff localEs) a -> Eff es a
localWriter LocalEnv localEs es
env = \case
  Tell w
w   -> w -> Eff es ()
forall w (es :: [(Type -> Type) -> Type -> Type]).
(Writer w :> es, Monoid w) =>
w -> Eff es ()
L.tell w
w
  Listen Eff localEs a
m -> LocalEnv localEs es
-> ((forall r. Eff localEs r -> Eff es r) -> Eff es (a, w))
-> Eff es (a, w)
forall (es :: [(Type -> Type) -> Type -> Type])
       (handlerEs :: [(Type -> Type) -> Type -> Type])
       (localEs :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, SharedSuffix es handlerEs) =>
LocalEnv localEs handlerEs
-> ((forall r. Eff localEs r -> Eff es r) -> Eff es a) -> Eff es a
localSeqUnlift LocalEnv localEs es
env (((forall r. Eff localEs r -> Eff es r) -> Eff es (a, w))
 -> Eff es (a, w))
-> ((forall r. Eff localEs r -> Eff es r) -> Eff es (a, w))
-> Eff es (a, w)
forall a b. (a -> b) -> a -> b
$ \forall r. Eff localEs r -> Eff es r
unlift -> Eff es a -> Eff es (a, w)
forall w (es :: [(Type -> Type) -> Type -> Type]) a.
(Writer w :> es, Monoid w) =>
Eff es a -> Eff es (a, w)
L.listen (Eff localEs a -> Eff es a
forall r. Eff localEs r -> Eff es r
unlift Eff localEs a
m)

----------------------------------------
-- Shared

-- | Run the 'Writer' effect and return the final value along with the final
-- output (via "Effectful.Writer.Static.Shared").
runWriterShared :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
runWriterShared :: Eff (Writer w : es) a -> Eff es (a, w)
runWriterShared = (Eff (Writer w : es) a -> Eff es (a, w))
-> EffectHandler (Writer w) (Writer w : es)
-> Eff (Writer w : es) a
-> Eff es (a, w)
forall (e :: (Type -> Type) -> Type -> Type)
       (handlerEs :: [(Type -> Type) -> Type -> Type]) a
       (es :: [(Type -> Type) -> Type -> Type]) b.
(DispatchOf e ~ 'Dynamic) =>
(Eff handlerEs a -> Eff es b)
-> EffectHandler e handlerEs -> Eff (e : es) a -> Eff es b
reinterpret Eff (Writer w : es) a -> Eff es (a, w)
forall w (es :: [(Type -> Type) -> Type -> Type]) a.
Monoid w =>
Eff (Writer w : es) a -> Eff es (a, w)
S.runWriter EffectHandler (Writer w) (Writer w : es)
forall w (es :: [(Type -> Type) -> Type -> Type])
       (localEs :: [(Type -> Type) -> Type -> Type]) a.
(Writer w :> es, Monoid w) =>
LocalEnv localEs es -> Writer w (Eff localEs) a -> Eff es a
sharedWriter

-- | Run the 'Writer' effect and return the final output, discarding the final
-- value (via "Effectful.Writer.Static.Shared").
execWriterShared :: Monoid w => Eff (Writer w : es) a -> Eff es w
execWriterShared :: Eff (Writer w : es) a -> Eff es w
execWriterShared = (Eff (Writer w : es) a -> Eff es w)
-> EffectHandler (Writer w) (Writer w : es)
-> Eff (Writer w : es) a
-> Eff es w
forall (e :: (Type -> Type) -> Type -> Type)
       (handlerEs :: [(Type -> Type) -> Type -> Type]) a
       (es :: [(Type -> Type) -> Type -> Type]) b.
(DispatchOf e ~ 'Dynamic) =>
(Eff handlerEs a -> Eff es b)
-> EffectHandler e handlerEs -> Eff (e : es) a -> Eff es b
reinterpret Eff (Writer w : es) a -> Eff es w
forall w (es :: [(Type -> Type) -> Type -> Type]) a.
Monoid w =>
Eff (Writer w : es) a -> Eff es w
S.execWriter EffectHandler (Writer w) (Writer w : es)
forall w (es :: [(Type -> Type) -> Type -> Type])
       (localEs :: [(Type -> Type) -> Type -> Type]) a.
(Writer w :> es, Monoid w) =>
LocalEnv localEs es -> Writer w (Eff localEs) a -> Eff es a
sharedWriter

sharedWriter
  :: (S.Writer w :> es, Monoid w)
  => LocalEnv localEs es
  -> Writer w (Eff localEs) a
  -> Eff es a
sharedWriter :: LocalEnv localEs es -> Writer w (Eff localEs) a -> Eff es a
sharedWriter LocalEnv localEs es
env = \case
  Tell w
w    -> w -> Eff es ()
forall w (es :: [(Type -> Type) -> Type -> Type]).
(Writer w :> es, Monoid w) =>
w -> Eff es ()
S.tell w
w
  Listen Eff localEs a
m  -> LocalEnv localEs es
-> ((forall r. Eff localEs r -> Eff es r) -> Eff es (a, w))
-> Eff es (a, w)
forall (es :: [(Type -> Type) -> Type -> Type])
       (handlerEs :: [(Type -> Type) -> Type -> Type])
       (localEs :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, SharedSuffix es handlerEs) =>
LocalEnv localEs handlerEs
-> ((forall r. Eff localEs r -> Eff es r) -> Eff es a) -> Eff es a
localSeqUnlift LocalEnv localEs es
env (((forall r. Eff localEs r -> Eff es r) -> Eff es (a, w))
 -> Eff es (a, w))
-> ((forall r. Eff localEs r -> Eff es r) -> Eff es (a, w))
-> Eff es (a, w)
forall a b. (a -> b) -> a -> b
$ \forall r. Eff localEs r -> Eff es r
unlift -> Eff es a -> Eff es (a, w)
forall w (es :: [(Type -> Type) -> Type -> Type]) a.
(Writer w :> es, Monoid w) =>
Eff es a -> Eff es (a, w)
S.listen (Eff localEs a -> Eff es a
forall r. Eff localEs r -> Eff es r
unlift Eff localEs a
m)

----------------------------------------
-- Operations

-- | Append the given output to the overall output of the 'Writer'.
tell
  :: (HasCallStack, Writer w :> es)
  => w
  -> Eff es ()
tell :: w -> Eff es ()
tell = Writer w (Eff es) () -> Eff es ()
forall (e :: (Type -> Type) -> Type -> Type)
       (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, DispatchOf e ~ 'Dynamic, e :> es) =>
e (Eff es) a -> Eff es a
send (Writer w (Eff es) () -> Eff es ())
-> (w -> Writer w (Eff es) ()) -> w -> Eff es ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. w -> Writer w (Eff es) ()
forall w (m :: Type -> Type). w -> Writer w m ()
Tell

-- | Execute an action and append its output to the overall output of the
-- 'Writer'.
listen
  :: (HasCallStack, Writer w :> es)
  => Eff es a
  -> Eff es (a, w)
listen :: Eff es a -> Eff es (a, w)
listen = Writer w (Eff es) (a, w) -> Eff es (a, w)
forall (e :: (Type -> Type) -> Type -> Type)
       (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, DispatchOf e ~ 'Dynamic, e :> es) =>
e (Eff es) a -> Eff es a
send (Writer w (Eff es) (a, w) -> Eff es (a, w))
-> (Eff es a -> Writer w (Eff es) (a, w))
-> Eff es a
-> Eff es (a, w)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Eff es a -> Writer w (Eff es) (a, w)
forall (m :: Type -> Type) a w. m a -> Writer w m (a, w)
Listen

-- | Execute an action and append its output to the overall output of the
-- 'Writer', then return the final value along with a function of the recorded
-- output.
--
-- @'listens' f m ≡ 'Data.Bifunctor.second' f '<$>' 'listen' m@
listens
  :: (HasCallStack, Writer w :> es)
  => (w -> b)
  -> Eff es a
  -> Eff es (a, b)
listens :: (w -> b) -> Eff es a -> Eff es (a, b)
listens w -> b
f Eff es a
m = do
  (a
a, w
w) <- Eff es a -> Eff es (a, w)
forall w (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Writer w :> es) =>
Eff es a -> Eff es (a, w)
listen Eff es a
m
  (a, b) -> Eff es (a, b)
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (a
a, w -> b
f w
w)