mirror of
https://github.com/haskell-effectful/effectful.git
synced 2024-11-27 11:44:45 +03:00
Add MVar and Dynamic version of Writer
This commit is contained in:
parent
26001d987a
commit
eb1e75e3e0
@ -83,6 +83,8 @@ library
|
|||||||
Effectful.State.MVar
|
Effectful.State.MVar
|
||||||
Effectful.STM
|
Effectful.STM
|
||||||
Effectful.Writer
|
Effectful.Writer
|
||||||
|
Effectful.Writer.Dynamic
|
||||||
|
Effectful.Writer.MVar
|
||||||
|
|
||||||
test-suite test
|
test-suite test
|
||||||
import: language
|
import: language
|
||||||
|
@ -6,7 +6,7 @@ import Control.Monad.Trans.Control
|
|||||||
|
|
||||||
import Effectful.Internal.Effect
|
import Effectful.Internal.Effect
|
||||||
import Effectful.Internal.Monad
|
import Effectful.Internal.Monad
|
||||||
import qualified Effectful.Writer as W
|
import qualified Effectful.Writer.Dynamic as W
|
||||||
|
|
||||||
-- | Compatiblity layer for a transition period from MTL-style effect handling
|
-- | Compatiblity layer for a transition period from MTL-style effect handling
|
||||||
-- to 'Effectful.Eff'.
|
-- to 'Effectful.Eff'.
|
||||||
|
88
src/Effectful/Writer/Dynamic.hs
Normal file
88
src/Effectful/Writer/Dynamic.hs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
module Effectful.Writer.Dynamic
|
||||||
|
( Writer(..)
|
||||||
|
|
||||||
|
-- * Pure
|
||||||
|
, runWriter
|
||||||
|
, execWriter
|
||||||
|
|
||||||
|
-- * MVar
|
||||||
|
, runWriterMVar
|
||||||
|
, execWriterMVar
|
||||||
|
|
||||||
|
-- * Operations
|
||||||
|
, writer
|
||||||
|
, tell
|
||||||
|
, listen
|
||||||
|
, listens
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Effectful.Internal.Effect
|
||||||
|
import Effectful.Internal.Monad
|
||||||
|
import Effectful.Interpreter
|
||||||
|
|
||||||
|
import qualified Effectful.Writer as WP
|
||||||
|
import qualified Effectful.Writer.MVar as WM
|
||||||
|
|
||||||
|
data Writer w :: Effect where
|
||||||
|
Writer :: (a, w) -> Writer w m a
|
||||||
|
Tell :: ~w -> Writer w m ()
|
||||||
|
Listen :: m a -> Writer w m (a, w)
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- Pure
|
||||||
|
|
||||||
|
runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
|
||||||
|
runWriter = reinterpretM WP.runWriter writerPure
|
||||||
|
|
||||||
|
execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w
|
||||||
|
execWriter = reinterpretM WP.execWriter writerPure
|
||||||
|
|
||||||
|
writerPure
|
||||||
|
:: (Monoid w, WP.Writer w :> localEs)
|
||||||
|
=> RunIn es (Eff localEs)
|
||||||
|
-> Writer w (Eff es) a
|
||||||
|
-> Eff localEs a
|
||||||
|
writerPure run = \case
|
||||||
|
Writer aw -> WP.writer aw
|
||||||
|
Tell w -> WP.tell w
|
||||||
|
Listen m -> WP.listen (run m)
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- MVar
|
||||||
|
|
||||||
|
runWriterMVar :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
|
||||||
|
runWriterMVar = reinterpretM WM.runWriter writerMVar
|
||||||
|
|
||||||
|
execWriterMVar :: Monoid w => Eff (Writer w : es) a -> Eff es w
|
||||||
|
execWriterMVar = reinterpretM WM.execWriter writerMVar
|
||||||
|
|
||||||
|
writerMVar
|
||||||
|
:: (Monoid w, WM.Writer w :> localEs)
|
||||||
|
=> RunIn es (Eff localEs)
|
||||||
|
-> Writer w (Eff es) a
|
||||||
|
-> Eff localEs a
|
||||||
|
writerMVar run = \case
|
||||||
|
Writer aw -> WM.writer aw
|
||||||
|
Tell w -> WM.tell w
|
||||||
|
Listen m -> WM.listen (run m)
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- Operations
|
||||||
|
|
||||||
|
writer :: (Writer w :> es, Monoid w) => (a, w) -> Eff es a
|
||||||
|
writer = send . Writer
|
||||||
|
|
||||||
|
tell :: (Writer w :> es, Monoid w) => w -> Eff es ()
|
||||||
|
tell = send . Tell
|
||||||
|
|
||||||
|
listen :: (Writer w :> es, Monoid w) => Eff es a -> Eff es (a, w)
|
||||||
|
listen = send . Listen
|
||||||
|
|
||||||
|
listens
|
||||||
|
:: (Writer w :> es, Monoid w)
|
||||||
|
=> (w -> b)
|
||||||
|
-> Eff es a
|
||||||
|
-> Eff es (a, b)
|
||||||
|
listens f m = do
|
||||||
|
(a, w) <- listen m
|
||||||
|
pure (a, f w)
|
71
src/Effectful/Writer/MVar.hs
Normal file
71
src/Effectful/Writer/MVar.hs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
module Effectful.Writer.MVar
|
||||||
|
( Writer
|
||||||
|
, runWriter
|
||||||
|
, execWriter
|
||||||
|
, writer
|
||||||
|
, tell
|
||||||
|
, listen
|
||||||
|
, listens
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Concurrent.MVar
|
||||||
|
import Control.Monad.Catch
|
||||||
|
|
||||||
|
import Effectful.Internal.Effect
|
||||||
|
import Effectful.Internal.Env
|
||||||
|
import Effectful.Internal.Monad
|
||||||
|
|
||||||
|
newtype Writer w :: Effect where
|
||||||
|
Writer :: MVar w -> Writer w m r
|
||||||
|
|
||||||
|
runWriter :: Monoid w => Eff (Writer w : es) a -> Eff es (a, w)
|
||||||
|
runWriter m = do
|
||||||
|
v <- unsafeEff_ $ newMVar mempty
|
||||||
|
a <- evalEffect (IdE (Writer v)) m
|
||||||
|
(a, ) <$> unsafeEff_ (readMVar v)
|
||||||
|
|
||||||
|
execWriter :: Monoid w => Eff (Writer w : es) a -> Eff es w
|
||||||
|
execWriter m = do
|
||||||
|
v <- unsafeEff_ $ newMVar mempty
|
||||||
|
_ <- evalEffect (IdE (Writer v)) m
|
||||||
|
unsafeEff_ $ readMVar v
|
||||||
|
|
||||||
|
writer :: (Writer w :> es, Monoid w) => (a, w) -> Eff es a
|
||||||
|
writer (a, w1) = do
|
||||||
|
IdE (Writer v) <- getEffect
|
||||||
|
unsafeEff_ . modifyMVar v $ \w0 -> let w = w0 `mappend` w1 in w `seq` pure (w, a)
|
||||||
|
|
||||||
|
tell :: (Writer w :> es, Monoid w) => w -> Eff es ()
|
||||||
|
tell w1 = do
|
||||||
|
IdE (Writer v) <- getEffect
|
||||||
|
unsafeEff_ . modifyMVar_ v $ \w0 -> let w = w0 `mappend` w1 in w `seq` pure w
|
||||||
|
|
||||||
|
listen
|
||||||
|
:: forall w es a. (Writer w :> es, Monoid w)
|
||||||
|
=> Eff es a
|
||||||
|
-> Eff es (a, w)
|
||||||
|
listen (Eff m) = unsafeEff $ \es -> uninterruptibleMask $ \restore -> do
|
||||||
|
v1 <- newMVar mempty
|
||||||
|
-- Replace thread local MVar with a fresh one for isolated listening.
|
||||||
|
v0 <- unsafeStateEnv (\(IdE (Writer v)) -> (v, IdE (Writer v1))) es
|
||||||
|
a <- restore (m es) `onException` merge es v0 v1
|
||||||
|
(a, ) <$> merge es v0 v1
|
||||||
|
where
|
||||||
|
-- Merge results accumulated in the local MVar with the mainline. If an
|
||||||
|
-- exception was received while listening, merge results recorded so far.
|
||||||
|
merge es v0 v1 = do
|
||||||
|
unsafePutEnv @(Writer w) (IdE (Writer v0)) es
|
||||||
|
w1 <- readMVar v1
|
||||||
|
-- The mask is uninterruptible because modifyMVar_ v0 might block and if
|
||||||
|
-- we get an async exception while waiting, w1 will be lost.
|
||||||
|
modifyMVar_ v0 $ \w0 -> let w = w0 `mappend` w1 in w `seq` pure w
|
||||||
|
pure w1
|
||||||
|
|
||||||
|
listens
|
||||||
|
:: (Writer w :> es, Monoid w)
|
||||||
|
=> (w -> b)
|
||||||
|
-> Eff es a
|
||||||
|
-> Eff es (a, b)
|
||||||
|
listens f m = do
|
||||||
|
(a, w) <- listen m
|
||||||
|
pure (a, f w)
|
Loading…
Reference in New Issue
Block a user