effectful-core
Safe HaskellSafe-Inferred
LanguageGHC2021

Effectful.Writer.Static.Shared

Description

Support for access to a write only value of a particular type.

The value is shared between multiple threads. If you want each thread to manage its own version of the value, use Effectful.Writer.Static.Local.

Warning: Writer's state will be accumulated via left-associated uses of <>, which makes it unsuitable for use with types for which such pattern is inefficient. This applies, in particular, to the standard list type, which makes the Writer effect pretty niche.

If you just want to accumulate values, use Effectful.Output.Static.Shared.Array or Effectful.Output.Static.Shared.List.

Note: while the Writer from the transformers package includes additional operations pass and censor, they don't cooperate with runtime exceptions very well, so they're deliberately omitted here.

Synopsis

Effect

data Writer (w :: Type) :: Effect Source #

Provide access to a strict (WHNF), shared, write only value of type w.

Instances

Instances details
type DispatchOf (Writer w) Source # 
Instance details

Defined in Effectful.Writer.Static.Shared

newtype StaticRep (Writer w) Source # 
Instance details

Defined in Effectful.Writer.Static.Shared

newtype StaticRep (Writer w) = Writer (MVar w)

Handlers

runWriter :: (HasCallStack, Monoid w) => Eff (Writer w : es) a -> Eff es (a, w) Source #

Run a Writer effect and return the final value along with the final output.

execWriter :: (HasCallStack, Monoid w) => Eff (Writer w : es) a -> Eff es w Source #

Run a Writer effect and return the final output, discarding the final value.

Operations

tell :: (HasCallStack, Writer w :> es, Monoid w) => w -> Eff es () Source #

Append the given output to the overall output of the Writer.

listen :: (HasCallStack, Writer w :> es, Monoid w) => Eff es a -> Eff es (a, w) Source #

Execute an action and append its output to the overall output of the Writer.

Note: the output of tell executed from threads spawned within the nested action is accounted for only if it completes before listen merges the output, which happens as soon as the action finishes. In particular, the output of threads that outlive the scope of listen will be lost:

>>> :{
  runEff . execWriter @String $ do
    lock <- liftIO newEmptyMVar
    done <- liftIO newEmptyMVar
    tell "1"
    _ <- listen @String $ do
      tell "2"
      withEffToIO (ConcUnlift Ephemeral $ Limited 1) $ \unlift -> do
        _ <- forkIO $ do
          takeMVar lock
          unlift $ tell "3"
          putMVar done ()
        pure ()
    liftIO $ putMVar lock ()
    liftIO $ takeMVar done
    tell "4"
:}
"124"

Note: if an exception is received while the action is executed, the partial output of the action will still be appended to the overall output of the Writer:

>>> :{
  runEff . execWriter @String $ do
    tell "Hi"
    handle (\(_::ErrorCall) -> pure ((), "")) $ do
      tell " there"
      listen $ do
        tell "!"
        error "oops"
:}
"Hi there!"

listens :: (HasCallStack, Writer w :> es, Monoid w) => (w -> b) -> Eff es a -> Eff es (a, b) Source #

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 ≡ second f <$> listen m