capability-0.2.0.0: Extensional capabilities and deriving combinators

Safe HaskellNone
LanguageHaskell2010

Capability.Writer

Contents

Description

Defines a capability type class for writer effects. A writer program can output values with tell. The values output by two consecutive sub-computation are combined using a monoid's mappend.

The interface of HasWriter follows that of MonadWriter. However, this module does not include a strategy to provide a HasWriter capability from a MonadWriter instance. It is generally a bad idea to use monads such as WriterT, as they tend to leak space, as described in this <https://blog.infinitenegativeutility.com/2016/7/writer-monads-and-space-leaks blog post> by Getty Ritter.

Instead, you should use the WriterLog strategy that implements the writer monad on a state monad. There is no downside, as using HasWriter instead of HasState directly ensures your code adheres to the writer monad interface and does not misuse the underlying state monad.

Synopsis

Interface

class (Monoid w, Monad m, HasStream tag w m) => HasWriter (tag :: k) (w :: *) (m :: * -> *) | tag m -> w where Source #

Writer capability

An instance should fulfill the following laws. At this point these laws are not definitive, see https://github.com/haskell/mtl/issues/5.

listen @t (pure a) = pure (a, mempty)
listen @t (tell @t w) = tell @t w >> pure (w, w)
listen @t (m >>= k) = listen @t m >>= \(a, w1) -> listen @t (k a) >>= \(b, w2) -> pure (b, w1 `mappend` w2)
pass @t (tell @t w >> pure (a, f)) = tell @t (f w) >> pure a
writer @t (a, w) = tell @t w >> pure a

A note on the HasStream super class.

HasStream offers one yield method with the same signature as tell. Many people's intuition, however, wouldn't connect the two: yielding tosses the value down some black-box chute, while telling grows and accumulation via the monoid. The connection is since the chute is opaque, the tosser cannot rule out there being such an accumulation at the chutes other end.

Formally, we reach the same conclusion. HasStream has no laws, indicating the user can make no assumptions beyond the signature of yield. HasWriter, with tell defined as yield, is thus always compatable regardless of whatever additional methods it provides and laws by which it abides.

Methods

writer_ :: Proxy# tag -> (a, w) -> m a Source #

For technical reasons, this method needs an extra proxy argument. You only need it if you are defining new instances of HasReader. Otherwise, you will want to use writer. See writer for more documentation.

listen_ :: Proxy# tag -> m a -> m (a, w) Source #

For technical reasons, this method needs an extra proxy argument. You only need it if you are defining new instances of HasReader. Otherwise, you will want to use listen. See listen for more documentation.

pass_ :: Proxy# tag -> m (a, w -> w) -> m a Source #

For technical reasons, this method needs an extra proxy argument. You only need it if you are defining new instances of HasReader. Otherwise, you will want to use pass. See pass for more documentation.

Instances
(Monoid w, HasState tag w m) => HasWriter (tag :: k) w (WriterLog m) Source # 
Instance details

Defined in Capability.Writer

Methods

writer_ :: Proxy# tag -> (a, w) -> WriterLog m a Source #

listen_ :: Proxy# tag -> WriterLog m a -> WriterLog m (a, w) Source #

pass_ :: Proxy# tag -> WriterLog m (a, w -> w) -> WriterLog m a Source #

(forall x. Coercible (m x) (t2 (t1 m) x), Monad m, HasWriter tag w (t2 (t1 m))) => HasWriter (tag :: k) w ((t2 :.: t1) m) Source #

Compose two accessors.

Instance details

Defined in Capability.Writer

Methods

writer_ :: Proxy# tag -> (a, w) -> (t2 :.: t1) m a Source #

listen_ :: Proxy# tag -> (t2 :.: t1) m a -> (t2 :.: t1) m (a, w) Source #

pass_ :: Proxy# tag -> (t2 :.: t1) m (a, w -> w) -> (t2 :.: t1) m a Source #

(HasWriter tag w m, MonadTransUnlift t, Monad (t m)) => HasWriter (tag :: Type) w (Lift (t m)) Source #

Lift one layer in a monad transformer stack.

Note, that if the HasWriter instance is based on HasState, then it is more efficient to apply Lift to the underlying state capability. E.g. you should favour

deriving (HasWriter tag w) via
  WriterLog (Lift (SomeTrans (MonadState SomeStateMonad)))

over

deriving (HasWriter tag w) via
  Lift (SomeTrans (WriterLog (MonadState SomeStateMonad)))
Instance details

Defined in Capability.Writer.Discouraged

Methods

writer_ :: Proxy# tag -> (a, w) -> Lift (t m) a Source #

listen_ :: Proxy# tag -> Lift (t m) a -> Lift (t m) (a, w) Source #

pass_ :: Proxy# tag -> Lift (t m) (a, w -> w) -> Lift (t m) a Source #

writer :: forall tag w m a. HasWriter tag w m => (a, w) -> m a Source #

writer @tag (a, w) lifts a pure writer action (a, w) to a monadic action in an arbitrary monad m with capability HasWriter.

Appends w to the output of the writer capability tag and returns the value a.

tell :: forall tag w m. HasWriter tag w m => w -> m () Source #

tell @tag w appends w to the output of the writer capability tag.

listen :: forall tag w m a. HasWriter tag w m => m a -> m (a, w) Source #

listen @tag m executes the action m and returns the output of m in the writer capability tag along with result of m. Appends the output of m to the output of the writer capability tag.

pass :: forall tag w m a. HasWriter tag w m => m (a, w -> w) -> m a Source #

pass @tag m executes the action m. Assuming m returns (a, f) and appends w to the output of the writer capability tag. pass @tag m instead appends w' = f w to the output and returns a.

Strategies

newtype StreamLog m (a :: *) Source #

Accumulate streamed values with their own monoid.

Constructors

StreamLog (m a) 
Instances
(Monoid w, HasState tag w m) => HasStream (tag :: k) w (StreamLog m) Source # 
Instance details

Defined in Capability.Stream

Methods

yield_ :: Proxy# tag -> w -> StreamLog m () Source #

(Monoid w, HasState tag w m) => HasWriter (tag :: k) w (WriterLog m) Source # 
Instance details

Defined in Capability.Writer

Methods

writer_ :: Proxy# tag -> (a, w) -> WriterLog m a Source #

listen_ :: Proxy# tag -> WriterLog m a -> WriterLog m (a, w) Source #

pass_ :: Proxy# tag -> WriterLog m (a, w -> w) -> WriterLog m a Source #

Monad m => Monad (StreamLog m) Source # 
Instance details

Defined in Capability.Stream

Methods

(>>=) :: StreamLog m a -> (a -> StreamLog m b) -> StreamLog m b #

(>>) :: StreamLog m a -> StreamLog m b -> StreamLog m b #

return :: a -> StreamLog m a #

fail :: String -> StreamLog m a #

Functor m => Functor (StreamLog m) Source # 
Instance details

Defined in Capability.Stream

Methods

fmap :: (a -> b) -> StreamLog m a -> StreamLog m b #

(<$) :: a -> StreamLog m b -> StreamLog m a #

Applicative m => Applicative (StreamLog m) Source # 
Instance details

Defined in Capability.Stream

Methods

pure :: a -> StreamLog m a #

(<*>) :: StreamLog m (a -> b) -> StreamLog m a -> StreamLog m b #

liftA2 :: (a -> b -> c) -> StreamLog m a -> StreamLog m b -> StreamLog m c #

(*>) :: StreamLog m a -> StreamLog m b -> StreamLog m b #

(<*) :: StreamLog m a -> StreamLog m b -> StreamLog m a #

MonadIO m => MonadIO (StreamLog m) Source # 
Instance details

Defined in Capability.Stream

Methods

liftIO :: IO a -> StreamLog m a #

PrimMonad m => PrimMonad (StreamLog m) Source # 
Instance details

Defined in Capability.Stream

Associated Types

type PrimState (StreamLog m) :: Type #

Methods

primitive :: (State# (PrimState (StreamLog m)) -> (#State# (PrimState (StreamLog m)), a#)) -> StreamLog m a #

type PrimState (StreamLog m) Source # 
Instance details

Defined in Capability.Stream

Modifiers