descript-lang-0.2.0.0: src/Core/Control/Monad/Trans.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE FlexibleInstances #-}
module Core.Control.Monad.Trans
( MonadHoist (..)
, MonadTransBridge (..)
, lift2
, rehoist
, mixinEffect
) where
import Data.Functor.Identity
import Control.Monad.Trans.Class
import Control.Monad.Trans.Writer.Strict
-- | Can transform the stacked version by transforming the unstacked
-- version, without knowing the unstacked version's type.
class (MonadTrans t) => MonadHoist t where
mapInner :: (forall b. w1 b -> w2 b) -> t w1 a -> t w2 a
-- | Bridges a monad and its transformer variant.
class (Monad u, MonadHoist t) => MonadTransBridge u t where
hoist :: (Monad w) => u a -> t w a
bindStackOuter :: (Monad w) => (u a -> t w b) -> t w a -> t w b
instance (Monoid r) => MonadHoist (WriterT r) where
mapInner = mapWriterT
instance (Monoid r) => MonadTransBridge (WriterT r Identity) (WriterT r) where
hoist = mapWriterT $ pure . runIdentity
bindStackOuter f = mapWriterT $ (runWriterT . f . writer =<<)
-- | Wraps an effect in 2 transformers.
lift2 :: (MonadTrans t1, MonadTrans t2, Monad u, Monad (t2 u))
=> u a
-> t1 (t2 u) a
lift2 = lift . lift
-- | Hoists the hoisted monad.
rehoist :: (MonadHoist t, MonadTransBridge wu wt, Monad ww)
=> t wu a
-> t (wt ww) a
rehoist = mapInner hoist
-- | Apply the effect, then return the original value.
mixinEffect :: (MonadTransBridge w wt, Monad we, Monad (wt we))
=> (w a -> we ())
-> w a
-> wt we a
mixinEffect eff x = lift (eff x) >> hoist x