packages feed

MonadCompose-0.1.0.0: Control/Monad/Distributive.hs

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}

module Control.Monad.Distributive (Distributive(dist)) where

import qualified Control.Monad.State.Strict as Strict
import Control.Monad.State
import Control.Monad.Writer
import Control.Monad.Reader
import Control.Monad.Identity
import Data.Functor.Compose

-- | Monads that distribute over one another.
class (Monad m, Monad n) => Distributive m n where
	dist :: m (n t) -> n (m t)

instance (Monad n) => Distributive Maybe n where
	dist (Just m) = liftM Just m
	dist Nothing = return Nothing

instance (Monad n) => Distributive (Either t) n where
	dist (Left x) = return (Left x)
	dist (Right m) = liftM Right m

instance (Monad n, Monoid x) => Distributive (WriterT x Identity) n where
	dist wr = let (m, w) = runWriter wr in m >>= \x -> return (tell w >> return x)

instance (Monad n) => Distributive [] n where
	dist = sequence

instance (Monad m) => Distributive m (Strict.StateT v Identity) where
	dist m = get >>= \x -> return (m >>= \st -> return $ Strict.evalState st x)

instance (Monad m) => Distributive m (StateT v Identity) where
	dist m = get >>= \x -> return (m >>= \st -> return $ evalState st x)

instance (Monad m) => Distributive m (ReaderT v Identity) where
	dist m = ask >>= \x -> return (m >>= \rd -> return $ runReader rd x)

instance (Monad m) => Distributive m Identity where
	dist m = Identity (m >>= return . runIdentity)

instance (Monad n) => Distributive Identity n where
	dist (Identity m) = m >>= return . Identity

instance (Distributive n2 n, Distributive m n2, Distributive m n) => Distributive m (Compose n n2) where
	dist = Compose . liftM dist . dist . liftM getCompose

instance (Distributive m n, Distributive m2 m, Distributive m2 n) => Distributive (Compose m m2) n where
	dist = liftM Compose . dist . liftM dist . getCompose

join' m = Compose $ join $ liftM (liftM join . dist) m

-- | Monads with a distributive law compose to give another monad.
instance (Distributive n m) => Monad (Compose m n) where
	return = Compose . return . return
	Compose m >>= f = join' (liftM (liftM (getCompose . f)) m)

instance (Monad m) => MonadTrans (Compose m) where
	lift = Compose . return