packages feed

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