@@ -43,6 +43,9 @@ module Benign
4343 NF (.. ),
4444 EvalIO (.. ),
4545 PureEval (.. ),
46+ -- * Monads
47+ EvalM (.. ),
48+ withSettingM ,
4649 )
4750where
4851
@@ -63,6 +66,10 @@ import GHC.Exts (Any)
6366import GHC.Stack (HasCallStack )
6467import System.IO.Unsafe
6568import Unsafe.Coerce
69+ import Data.Functor.Identity
70+ import Data.Coerce
71+ import Control.Monad.Trans.State.Lazy
72+ import Control.Monad.Trans.Reader
6673
6774newtype Field a = MkField Unique
6875 deriving newtype (Eq , Ord )
@@ -239,6 +246,12 @@ class Eval a where
239246
240247 extractEval :: Thunk a -> Result a
241248
249+ instance (Eval a , Eval b ) => Eval (a , b ) where
250+ data Thunk (a , b ) = PairThunk (Thunk a ) (Thunk b )
251+ type Result (a , b ) = (Result a , Result b )
252+ eval (a , b ) = PairThunk (eval a ) (eval b )
253+ extractEval (PairThunk a b ) = (extractEval a , extractEval b )
254+
242255-- | Evaluation strategy: evaluates `a` by simply calling `seq` on it.
243256newtype Seq a = Seq a
244257 deriving anyclass (EvalIO )
@@ -335,3 +348,45 @@ newtype PureEval a = PureEval a
335348instance Eval a => EvalIO (PureEval a ) where
336349 type ResultIO (PureEval a ) = Result a
337350 evalIO (PureEval a) = evalInIO a
351+
352+ ---------------------------------------------------------------------------
353+ --
354+ -- Evaluate in a monad
355+ --
356+ ---------------------------------------------------------------------------
357+
358+ -- | In non-IO monadic code (that is when monads are used as a way to organise
359+ -- pure code), naturally, we'll be wanting to use benign effect as well. How
360+ -- scopes and running monadic code interleave doesn't have a generic
361+ -- answer. This is because monadic code is fundamentally staged: first you build
362+ -- a monadic expression, then it is run. Benign effects, and in particular
363+ -- local state updates, must happen when the monad is run, not when the
364+ -- expression is built.
365+ --
366+ -- Just like there isn't a generic `run` function, since all monads interpret
367+ -- the monadic expression differently, each monad needs to explain how they
368+ -- implement 'withAltering' and 'unsafeSpanBenign'. This is what the (admittedly
369+ -- poorly named) 'EvalM' class lets monad do.
370+ class EvalM m where
371+ withAlteringM :: Eval b => Field a -> (Maybe a -> Maybe a ) -> m b -> m (Result b )
372+ unsafeSpanBenignM :: Eval a => IO () -> IO () -> m a -> m (Result a )
373+
374+ withSettingM :: (EvalM m , Eval b ) => Field a -> a -> m b -> m (Result b )
375+ withSettingM f a = withAlteringM f (\ _ -> Just a)
376+
377+ instance EvalM Identity where
378+ withAlteringM :: forall b a . Eval b => Field a -> (Maybe a -> Maybe a ) -> Identity b -> Identity (Result b )
379+ withAlteringM = coerce $ withAltering @ b @ a
380+ unsafeSpanBenignM :: forall a . Eval a => IO () -> IO () -> Identity a -> Identity (Result a )
381+ unsafeSpanBenignM = coerce $ unsafeSpanBenign @ a
382+
383+ instance (EvalM m , Eval s , Result s ~ s ) => EvalM (StateT s m ) where
384+ withAlteringM f g (StateT thing) = StateT $ \ s -> withAlteringM f g (thing s)
385+
386+ unsafeSpanBenignM :: (EvalM m , Eval s , Result s ~ s , Eval a ) => IO () -> IO () -> StateT s m a -> StateT s m (Result a )
387+ unsafeSpanBenignM before after (StateT thing) = StateT $ \ s -> unsafeSpanBenignM before after (thing s)
388+
389+ instance (EvalM m ) => EvalM (ReaderT e m ) where
390+ withAlteringM f g (ReaderT thing) = ReaderT $ \ e -> withAlteringM f g (thing e)
391+
392+ unsafeSpanBenignM before after (ReaderT thing) = ReaderT $ \ e -> unsafeSpanBenignM before after (thing e)
0 commit comments