{-# LANGUAGE BlockArguments, DerivingVia #-}
module Control.Effect.Writer
(
Tell(..)
, Listen(..)
, Pass(..)
, Writer
, tell
, listen
, pass
, censor
, runTell
, runTellLazy
, runTellList
, runTellListLazy
, tellToIO
, runTellIORef
, runTellTVar
, tellIntoEndoTell
, tellToTell
, tellIntoTell
, tellToIOSimple
, runTellIORefSimple
, runTellTVarSimple
, tellToTellSimple
, tellIntoTellSimple
, runListen
, runListenLazy
, listenToIO
, runListenTVar
, listenIntoEndoListen
, runWriter
, runWriterLazy
, writerToIO
, runWriterTVar
, writerToBracket
, writerToBracketTVar
, writerIntoEndoWriter
, fromEndoWriter
, WriterThreads
, WriterLazyThreads
, C.MonadMask
, TellC
, TellLazyC
, TellListC
, TellListLazyC
, TellIntoEndoTellC
, ListenC
, ListenLazyC
, ListenTVarC
, ListenIntoEndoListenC
, WriterC
, WriterLazyC
, WriterTVarC
, WriterToBracketC
, WriterIntoEndoWriterC
) where
import Data.Bifunctor
import Data.Semigroup
import Data.Tuple (swap)
import Data.IORef
import Control.Concurrent.STM
import Control.Monad
import Control.Effect
import Control.Effect.Reader
import Control.Effect.Bracket
import Control.Effect.Type.ListenPrim
import Control.Effect.Type.WriterPrim
import Control.Effect.Carrier
import Control.Effect.Internal.Writer
import qualified Control.Monad.Catch as C
import qualified Control.Monad.Trans.Writer.CPS as W
import qualified Control.Monad.Trans.Writer.Lazy as LW
import Control.Effect.Internal.Utils
import Control.Effect.Carrier.Internal.Interpret
import Control.Effect.Carrier.Internal.Compose
import Control.Effect.Carrier.Internal.Intro
import Control.Monad.Trans.Identity
type Writer s = Bundle '[Tell s, Listen s, Pass s]
tell :: Eff (Tell s) m => s -> m ()
tell = send . Tell
{-# INLINE tell #-}
listen :: Eff (Listen s) m => m a -> m (s, a)
listen = send . Listen
{-# INLINE listen #-}
pass :: Eff (Pass s) m => m (s -> s, a) -> m a
pass = send . Pass
{-# INLINE pass #-}
censor :: Eff (Pass s) m => (s -> s) -> m a -> m a
censor f = pass . fmap ((,) f)
{-# INLINE censor #-}
data TellListH
type TellListC s = CompositionC
'[ ReinterpretC TellListH (Tell s) '[Tell (Dual [s])]
, TellC (Dual [s])
]
instance Eff (Tell (Dual [s])) m
=> Handler TellListH (Tell s) m where
effHandler (Tell s) = tell (Dual [s])
{-# INLINEABLE effHandler #-}
runTellList :: forall s m a p
. ( Carrier m
, Threaders '[WriterThreads] m p
)
=> TellListC s m a
-> m ([s], a)
runTellList =
(fmap . first) (reverse .# getDual)
. runTell
.# reinterpretViaHandler
.# runComposition
{-# INLINE runTellList #-}
data TellListLazyH
type TellListLazyC s = CompositionC
'[ ReinterpretC TellListLazyH (Tell s) '[Tell (Endo [s])]
, TellLazyC (Endo [s])
]
instance Eff (Tell (Endo [s])) m
=> Handler TellListLazyH (Tell s) m where
effHandler (Tell s) = tell (Endo (s:))
{-# INLINEABLE effHandler #-}
runTellListLazy :: forall s m a p
. ( Carrier m
, Threaders '[WriterLazyThreads] m p
)
=> TellListLazyC s m a
-> m ([s], a)
runTellListLazy =
fromEndoWriter
. runTellLazy
.# reinterpretViaHandler
.# runComposition
{-# INLINE runTellListLazy #-}
runTell :: forall s m a p
. ( Monoid s
, Carrier m
, Threaders '[WriterThreads] m p
)
=> TellC s m a
-> m (s, a)
runTell (TellC m) = do
(a, s) <- W.runWriterT m
return (s, a)
{-# INLINE runTell #-}
runListen :: forall s m a p
. ( Monoid s
, Carrier m
, Threaders '[WriterThreads] m p
)
=> ListenC s m a
-> m (s, a)
runListen (ListenC m) = do
(a, s) <- W.runWriterT m
return (s, a)
{-# INLINE runListen #-}
runWriter :: forall s m a p
. ( Monoid s
, Carrier m
, Threaders '[WriterThreads] m p
)
=> WriterC s m a
-> m (s, a)
runWriter (WriterC m) = do
(a, s) <- W.runWriterT m
return (s, a)
{-# INLINE runWriter #-}
runTellLazy :: forall s m a p
. ( Monoid s
, Carrier m
, Threaders '[WriterLazyThreads] m p
)
=> TellLazyC s m a
-> m (s, a)
runTellLazy (TellLazyC m) = swap <$> LW.runWriterT m
{-# INLINE runTellLazy #-}
runListenLazy :: forall s m a p
. ( Monoid s
, Carrier m
, Threaders '[WriterThreads] m p
)
=> ListenLazyC s m a
-> m (s, a)
runListenLazy (ListenLazyC m) = swap <$> LW.runWriterT m
{-# INLINE runListenLazy #-}
runWriterLazy :: forall s m a p
. ( Monoid s
, Carrier m
, Threaders '[WriterLazyThreads] m p
)
=> WriterLazyC s m a
-> m (s, a)
runWriterLazy (WriterLazyC m) = swap <$> LW.runWriterT m
{-# INLINE runWriterLazy #-}
tellTVar :: ( Monoid s
, Effs '[Reader (s -> STM ()), Embed IO] m
)
=> s
-> m ()
tellTVar o = do
write <- ask
embed $ atomically $ write o
{-# INLINE tellTVar #-}
data WriterToEndoWriterH
instance (Monoid s, Eff (Tell (Endo s)) m)
=> Handler WriterToEndoWriterH (Tell s) m where
effHandler (Tell s) = tell (Endo (s <>))
{-# INLINEABLE effHandler #-}
instance (Monoid s, Eff (Listen (Endo s)) m)
=> Handler WriterToEndoWriterH (Listen s) m where
effHandler (Listen m) =
(fmap . first) (\(Endo f) -> f mempty) $ listen m
{-# INLINEABLE effHandler #-}
instance (Monoid s, Eff (Pass (Endo s)) m)
=> Handler WriterToEndoWriterH (Pass s) m where
effHandler (Pass m) =
pass $
(fmap . first)
(\f (Endo ss) -> let !s' = f (ss mempty) in Endo (s' <>))
m
{-# INLINEABLE effHandler #-}
fromEndoWriter :: (Monoid s, Functor f)
=> f (Endo s, a)
-> f (s, a)
fromEndoWriter = (fmap . first) (\(Endo f) -> f mempty)
{-# INLINE fromEndoWriter #-}
type TellIntoEndoTellC s =
ReinterpretC WriterToEndoWriterH (Tell s) '[Tell (Endo s)]
tellIntoEndoTell :: ( Monoid s
, HeadEff (Tell (Endo s)) m
)
=> TellIntoEndoTellC s m a
-> m a
tellIntoEndoTell = reinterpretViaHandler
{-# INLINE tellIntoEndoTell #-}
type ListenIntoEndoListenC s = CompositionC
'[ IntroC '[Listen s, Tell s] '[Listen (Endo s), Tell (Endo s)]
, InterpretC WriterToEndoWriterH (Listen s)
, InterpretC WriterToEndoWriterH (Tell s)
]
listenIntoEndoListen :: ( Monoid s
, HeadEffs '[Listen (Endo s), Tell (Endo s)] m
)
=> ListenIntoEndoListenC s m a
-> m a
listenIntoEndoListen =
interpretViaHandler
.# interpretViaHandler
.# introUnderMany
.# runComposition
{-# INLINE listenIntoEndoListen #-}
type WriterIntoEndoWriterC s = CompositionC
'[ IntroC '[Pass s, Listen s, Tell s]
'[Pass (Endo s), Listen (Endo s), Tell (Endo s)]
, InterpretC WriterToEndoWriterH (Pass s)
, InterpretC WriterToEndoWriterH (Listen s)
, InterpretC WriterToEndoWriterH (Tell s)
]
writerIntoEndoWriter :: ( Monoid s
, HeadEffs
'[Pass (Endo s), Listen (Endo s), Tell (Endo s)]
m
)
=> WriterIntoEndoWriterC s m a
-> m a
writerIntoEndoWriter =
interpretViaHandler
.# interpretViaHandler
.# interpretViaHandler
.# introUnderMany
.# runComposition
{-# INLINE writerIntoEndoWriter #-}
tellToTell :: forall s t m a
. Eff (Tell t) m
=> (s -> t)
-> InterpretReifiedC (Tell s) m a
-> m a
tellToTell f = interpret $ \case
Tell s -> tell (f s)
{-# INLINE tellToTell #-}
tellToTellSimple :: forall s t m a p
. ( Eff (Tell t) m
, Threaders '[ReaderThreads] m p
)
=> (s -> t)
-> InterpretSimpleC (Tell s) m a
-> m a
tellToTellSimple f = interpretSimple $ \case
Tell s -> tell (f s)
{-# INLINE tellToTellSimple #-}
tellIntoTell :: forall s t m a
. HeadEff (Tell t) m
=> (s -> t)
-> ReinterpretReifiedC (Tell s) '[Tell t] m a
-> m a
tellIntoTell f = reinterpret $ \case
Tell s -> tell (f s)
{-# INLINE tellIntoTell #-}
tellIntoTellSimple :: forall s t m a p
. ( HeadEff (Tell t) m
, Threaders '[ReaderThreads] m p
)
=> (s -> t)
-> ReinterpretSimpleC (Tell s) '[Tell t] m a
-> m a
tellIntoTellSimple f = reinterpretSimple $ \case
Tell s -> tell (f s)
{-# INLINE tellIntoTellSimple #-}
listenTVar :: forall s m a
. ( Monoid s
, Effs '[Reader (s -> STM ()), Embed IO, Bracket] m
)
=> m a
-> m (s, a)
listenTVar main = do
writeGlobal <- ask
localVar <- embed $ newTVarIO mempty
switch <- embed $ newTVarIO True
let
writeLocal :: s -> STM ()
writeLocal o = do
writeToLocal <- readTVar switch
when writeToLocal $ do
s <- readTVar localVar
writeTVar localVar $! s <> o
writeGlobal o
a <- (local (\_ -> writeLocal) main)
`finally`
(embed $ atomically $ writeTVar switch False)
s <- embed $ readTVarIO localVar
return (s, a)
passTVar :: forall s m a
. ( Monoid s
, Effs '[Reader (s -> STM ()), Embed IO, Bracket] m
)
=> m (s -> s, a)
-> m a
passTVar main = do
writeGlobal <- ask
localVar <- embed $ newTVarIO mempty
switch <- embed $ newTVarIO True
let
writeLocal :: s -> STM ()
writeLocal o = do
writeToLocal <- readTVar switch
if writeToLocal then do
s <- readTVar localVar
writeTVar localVar $! s <> o
else
writeGlobal o
commit :: (s -> s) -> IO ()
commit f = atomically $ do
notAlreadyCommited <- readTVar switch
when notAlreadyCommited $ do
s <- readTVar localVar
writeGlobal (f s)
writeTVar switch False
((_, a), _) <-
generalBracket
(pure ())
(\_ -> \case
ExitCaseSuccess (f, _) -> embed (commit f)
_ -> embed (commit id)
)
(\_ -> local (\_ -> writeLocal) main)
return a
data WriterToBracketH
type WriterToBracketC s = CompositionC
'[ IntroC '[Pass s, Listen s, Tell s] '[Local (s -> STM ()), Ask (s -> STM ())]
, InterpretC WriterToBracketH (Pass s)
, InterpretC WriterToBracketH (Listen s)
, InterpretC WriterTVarH (Tell s)
, ReaderC (s -> STM ())
]
instance ( Monoid s
, Effs '[Reader (s -> STM ()), Embed IO, Bracket] m
)
=> Handler WriterToBracketH (Listen s) m where
effHandler (Listen m) = listenTVar m
{-# INLINEABLE effHandler #-}
instance ( Monoid s
, Effs '[Reader (s -> STM ()), Embed IO, Bracket] m
)
=> Handler WriterToBracketH (Pass s) m where
effHandler (Pass m) = passTVar m
{-# INLINEABLE effHandler #-}
writerToBracket :: forall s m a p
. ( Monoid s
, Effs [Embed IO, Bracket] m
, Threaders '[ReaderThreads] m p
)
=> WriterToBracketC s m a
-> m (s, a)
writerToBracket m = do
tvar <- embed $ newTVarIO mempty
a <- writerToBracketTVar tvar m
s <- embed $ readTVarIO tvar
return (s, a)
{-# INLINE writerToBracket #-}
writerToBracketTVar :: forall s m a p
. ( Monoid s
, Effs [Embed IO, Bracket] m
, Threaders '[ReaderThreads] m p
)
=> TVar s
-> WriterToBracketC s m a
-> m a
writerToBracketTVar tvar =
runReader (\o -> do
s <- readTVar tvar
writeTVar tvar $! s <> o
)
.# interpretViaHandler
.# interpretViaHandler
.# interpretViaHandler
.# introUnderMany
.# runComposition
{-# INLINE writerToBracketTVar #-}
data WriterTVarH
type ListenTVarC s = CompositionC
'[ IntroC '[Listen s, Tell s]
'[ ListenPrim s
, Local (s -> STM ())
, Ask (s -> STM ())
]
, InterpretC WriterTVarH (Listen s)
, InterpretC WriterTVarH (Tell s)
, InterpretPrimC WriterTVarH (ListenPrim s)
, ReaderC (s -> STM ())
]
type WriterTVarC s = CompositionC
'[ IntroC '[Pass s, Listen s, Tell s]
'[ ListenPrim s
, WriterPrim s
, Local (s -> STM ())
, Ask (s -> STM ())
]
, InterpretC WriterTVarH (Pass s)
, InterpretC WriterTVarH (Listen s)
, InterpretC WriterTVarH (Tell s)
, InterpretC WriterTVarH (ListenPrim s)
, InterpretPrimC WriterTVarH (WriterPrim s)
, ReaderC (s -> STM ())
]
instance ( Monoid s
, Effs '[Reader (s -> STM ()), Embed IO] m
)
=> Handler WriterTVarH (Tell s) m where
effHandler (Tell o) = tellTVar o
{-# INLINEABLE effHandler #-}
instance Eff (ListenPrim s) m
=> Handler WriterTVarH (Listen s) m where
effHandler (Listen m) = send $ ListenPrimListen m
{-# INLINEABLE effHandler #-}
instance Eff (WriterPrim s) m
=> Handler WriterTVarH (Pass s) m where
effHandler (Pass m) = send $ WriterPrimPass m
{-# INLINEABLE effHandler #-}
instance Eff (WriterPrim s) m
=> Handler WriterTVarH (ListenPrim s) m where
effHandler = \case
ListenPrimTell o -> send $ WriterPrimTell o
ListenPrimListen m -> send $ WriterPrimListen m
{-# INLINEABLE effHandler #-}
instance ( Monoid s
, Effs '[Reader (s -> STM ()), Embed IO] m
, C.MonadMask m
)
=> PrimHandler WriterTVarH (ListenPrim s) m where
effPrimHandler = \case
ListenPrimTell o -> tellTVar o
ListenPrimListen m -> bracketToIO (listenTVar (lift m))
{-# INLINEABLE effPrimHandler #-}
instance ( Monoid s
, Effs '[Reader (s -> STM ()), Embed IO] m
, C.MonadMask m
)
=> PrimHandler WriterTVarH (WriterPrim s) m where
effPrimHandler = \case
WriterPrimTell o -> tellTVar o
WriterPrimListen m -> bracketToIO (listenTVar (lift m))
WriterPrimPass m -> bracketToIO (passTVar (lift m))
{-# INLINEABLE effPrimHandler #-}
tellToIO :: forall s m a
. ( Monoid s
, Eff (Embed IO) m
)
=> InterpretReifiedC (Tell s) m a
-> m (s, a)
tellToIO m = do
ref <- embed $ newIORef mempty
a <- runTellIORef ref m
s <- embed $ readIORef ref
return (s, a)
{-# INLINE tellToIO #-}
runTellIORef :: forall s m a
. ( Monoid s
, Eff (Embed IO) m
)
=> IORef s
-> InterpretReifiedC (Tell s) m a
-> m a
runTellIORef ref = interpret $ \case
Tell o -> embed $ atomicModifyIORef' ref (\s -> (s <> o, ()))
{-# INLINE runTellIORef #-}
runTellTVar :: forall s m a
. ( Monoid s
, Eff (Embed IO) m
)
=> TVar s
-> InterpretReifiedC (Tell s) m a
-> m a
runTellTVar tvar = interpret $ \case
Tell o -> embed $ atomically $ do
s <- readTVar tvar
writeTVar tvar $! s <> o
{-# INLINE runTellTVar #-}
tellToIOSimple :: forall s m a p
. ( Monoid s
, Eff (Embed IO) m
, Threaders '[ReaderThreads] m p
)
=> InterpretSimpleC (Tell s) m a
-> m (s, a)
tellToIOSimple m = do
ref <- embed $ newIORef mempty
a <- runTellIORefSimple ref m
s <- embed $ readIORef ref
return (s, a)
{-# INLINE tellToIOSimple #-}
runTellIORefSimple :: forall s m a p
. ( Monoid s
, Eff (Embed IO) m
, Threaders '[ReaderThreads] m p
)
=> IORef s
-> InterpretSimpleC (Tell s) m a
-> m a
runTellIORefSimple ref = interpretSimple $ \case
Tell o -> embed $ atomicModifyIORef' ref (\s -> (s <> o, ()))
{-# INLINE runTellIORefSimple #-}
runTellTVarSimple :: forall s m a p
. ( Monoid s
, Eff (Embed IO) m
, Threaders '[ReaderThreads] m p
)
=> TVar s
-> InterpretSimpleC (Tell s) m a
-> m a
runTellTVarSimple tvar = interpretSimple $ \case
Tell o -> embed $ atomically $ do
s <- readTVar tvar
writeTVar tvar $! s <> o
{-# INLINE runTellTVarSimple #-}
listenToIO :: forall s m a p
. ( Monoid s
, Eff (Embed IO) m
, C.MonadMask m
, Threaders '[ReaderThreads] m p
)
=> ListenTVarC s m a
-> m (s, a)
listenToIO m = do
tvar <- embed $ newTVarIO mempty
a <- runListenTVar tvar m
s <- embed $ readTVarIO tvar
return (s, a)
{-# INLINE listenToIO #-}
runListenTVar :: forall s m a p
. ( Monoid s
, Eff (Embed IO) m
, C.MonadMask m
, Threaders '[ReaderThreads] m p
)
=> TVar s
-> ListenTVarC s m a
-> m a
runListenTVar tvar =
runReader (\o -> do
s <- readTVar tvar
writeTVar tvar $! s <> o
)
.# interpretPrimViaHandler
.# interpretViaHandler
.# interpretViaHandler
.# introUnderMany
.# runComposition
{-# INLINE runListenTVar #-}
writerToIO :: forall s m a p
. ( Monoid s
, Eff (Embed IO) m
, C.MonadMask m
, Threaders '[ReaderThreads] m p
)
=> WriterTVarC s m a
-> m (s, a)
writerToIO m = do
tvar <- embed $ newTVarIO mempty
a <- runWriterTVar tvar m
s <- embed $ readTVarIO tvar
return (s, a)
{-# INLINE writerToIO #-}
runWriterTVar :: forall s m a p
. ( Monoid s
, Eff (Embed IO) m
, C.MonadMask m
, Threaders '[ReaderThreads] m p
)
=> TVar s
-> WriterTVarC s m a
-> m a
runWriterTVar tvar =
runReader (\o -> do
s <- readTVar tvar
writeTVar tvar $! s <> o
)
.# interpretPrimViaHandler
.# interpretViaHandler
.# interpretViaHandler
.# interpretViaHandler
.# interpretViaHandler
.# introUnderMany
.# runComposition
{-# INLINE runWriterTVar #-}