effin-0.3.0.1: src/Control/Effect/Writer.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
#if MTL
{-# OPTIONS_GHC -fno-warn-orphans #-}
#endif
module Control.Effect.Writer (
EffectWriter, Writer, runWriter,
tell, listen, listens, pass, censor,
stateWriter
) where
import Control.Monad.Effect
import Control.Arrow (second)
#if !MIN_VERSION_base(4, 8, 0)
import Data.Monoid (Monoid (..))
#endif
import Control.Effect.State
#ifdef MTL
import Data.Type.Row
import qualified Control.Monad.Writer.Class as W
instance (Monoid w, Member (Writer w) l, Writer w ~ InstanceOf Writer l) => W.MonadWriter w (Effect l) where
tell = tell
listen = listen
pass = pass
#endif
-- | An effect that allows accumulating output.
data Writer w a where
Tell :: w -> Writer w ()
type instance Is Writer f = IsWriter f
type family IsWriter f where
IsWriter (Writer w) = 'True
IsWriter f = 'False
class (Monoid w, MemberEffect Writer (Writer w) l) => EffectWriter w l
instance (Monoid w, MemberEffect Writer (Writer w) l) => EffectWriter w l
-- | Writes a value to the output.
tell :: EffectWriter w l => w -> Effect l ()
tell = send . Tell
-- | Executes a computation, and obtains the writer output.
-- The writer output of the inner computation is still
-- written to the writer output of the outer computation.
listen :: EffectWriter w l => Effect l a -> Effect l (a, w)
listen effect = do
value@(_, output) <- intercept point bind effect
tell output
return value
-- | Like `listen`, but the writer output is run through a function.
listens :: EffectWriter w l => (w -> b) -> Effect l a -> Effect l (a, b)
listens f = fmap (second f) . listen
-- | Runs a computation that returns a value and a function,
-- applies the function to the writer output, and then returns the value.
pass :: EffectWriter w l => Effect l (a, w -> w) -> Effect l a
pass effect = do
((x, f), l) <- listen effect
tell (f l)
return x
-- | Applies a function to the writer output of a computation.
censor :: EffectWriter w l => (w -> w) -> Effect l a -> Effect l a
censor f effect = pass $ do
a <- effect
return (a, f)
-- | Executes a writer computation which sends its output to a state effect.
stateWriter :: (Monoid s, EffectState s l) => Effect (Writer s ':+ l) a -> Effect l a
stateWriter = eliminate return (\(Tell l) k -> modify (mappend l) >> k ())
-- | Completely handles a writer effect. The writer value must be a `Monoid`.
-- `mempty` is used as an initial value, and `mappend` is used to combine values.
-- Returns the result of the computation and the final output value.
runWriter :: Monoid w => Effect (Writer w ':+ l) a -> Effect l (a, w)
runWriter = eliminate point bind
point :: Monoid w => a -> Effect l (a, w)
point x = return (x, mempty)
bind :: Monoid w => Writer w a -> (a -> Effect l (b, w)) -> Effect l (b, w)
bind (Tell l) k = second (mappend l) `fmap` k ()