packages feed

MonadCompose-0.4.0.0: Control/Monad/Lifter.hs

{-# LANGUAGE IncoherentInstances, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeOperators, GeneralizedNewtypeDeriving, FunctionalDependencies, UndecidableInstances #-}

module Control.Monad.Lifter where

import Control.Monad.Plus
import Control.Monad.Trans
import Control.Monad.ST
import Control.Monad.Identity
import Control.Monad.Reader
import Control.Applicative
import Control.Monad.Morph

-- | An automatic lifter. The idea of automatic lifting is due to Dan Piponi.
class Lifter m n where
	lift' :: m t -> n t

instance Lifter (ST RealWorld) IO where
	lift' = stToIO

instance Lifter IO IO where
	lift' = id

instance Lifter (ST s) (ST s) where
	lift' = id

instance Lifter Identity Identity where
	lift' = id

instance (MonadTrans n, Monad x, Lifter m x) => Lifter m (n x) where
	lift' = lift . lift'

instance (Monad x, MFunctor m) => Lifter (m Identity) (m x) where
	lift' = hoist (return . runIdentity)

instance (Lifter m x) => Lifter m (x ::+ n) where
	lift' = inl . lift'

-- | Declared lifts.
newtype Lifted m t = Lifted { runLifted :: m t } deriving (Monad, Applicative, Functor)

instance MFunctor Lifted where
	hoist f (Lifted m) = Lifted (f m)

class Convert m n | m -> n where
	convert :: m t -> Lifted n t

instance Convert (Lifted m) m where
	convert = id

instance (Lifter IO n) => Convert IO n where
	convert = Lifted . lift'

instance (Lifter (ST s) n) => Convert (ST s) n where
	convert = Lifted . lift'

instance (Lifter (ReaderT r m) n) => Convert (ReaderT r m) n where
	convert = Lifted . lift'

infixl 1 >-=
infixl 1 >-

-- Explicitly declared lifts are needed here, so I can declare a special-case lift for
-- Lifted. Lifted and its special case are needed so that I know how to unify types
-- through nested uses of >-=.
--
-- | Auto-lifting versions of monadic bind.
m >-= f = convert m >>= convert . f

m >- m2 = convert m >> convert m2