module FP.Monads where

import FP.Core

---------------------
-- Monadic Effects --
---------------------

-- ID {{{

newtype ID a = ID { unID :: a }
  deriving
  ( Eq, Ord
  , PartialOrder
  , Monoid
  , Bot
  , Top
  , Join
  , JoinLattice
  )

instance Unit ID where unit = ID
instance Functor ID where map f = ID . f . unID
instance FunctorM ID where mapM f = map ID . f . unID
instance Product ID where aM <*> bM = ID $ (unID aM, unID bM)
instance Applicative ID where fM <@> aM = ID $ unID fM $ unID aM
instance Bind ID where aM >>= k = k $ unID aM
instance Monad ID
instance Functorial Bot ID where functorial = W
instance Functorial Top ID where functorial = W
instance Functorial Join ID where functorial = W
instance Functorial JoinLattice ID where functorial = W
instance Functorial Monoid ID where functorial = W

newtype IDT m a = IDT { unIDT :: m a }

-- }}}

-- MaybeT {{{

maybeCommute :: (Functor m) => MaybeT (MaybeT m) ~> MaybeT (MaybeT m)
maybeCommute aMM = MaybeT $ MaybeT $ ff ^$ unMaybeT $ unMaybeT aMM
  where
    ff Nothing = Just Nothing
    ff (Just Nothing) = Nothing
    ff (Just (Just a)) = Just (Just a)
  
instance (Unit m) => Unit (MaybeT m) where unit = MaybeT . unit . Just
instance (Functor m) => Functor (MaybeT m) where map f = MaybeT . f ^^. unMaybeT
instance (Functor m, Product m) => Product (MaybeT m) where
  aM1 <*> aM2 = MaybeT $ uncurry ff ^$ unMaybeT aM1 <*> unMaybeT aM2
    where
      ff Nothing _ = Nothing
      ff _ Nothing = Nothing
      ff (Just a1) (Just a2) = Just (a1, a2)
instance (Functor m, Applicative m) => Applicative (MaybeT m) where
  fM <@> aM = MaybeT $ ff ^@ unMaybeT fM <$> unMaybeT aM
    where
      ff Nothing _ = Nothing
      ff _ Nothing = Nothing
      ff (Just f) (Just a) = Just $ f a
instance (Monad m) => Bind (MaybeT m) where
  aM >>= k = MaybeT $ do
    aM' <- unMaybeT aM
    case aM' of
      Nothing -> return Nothing
      Just a -> unMaybeT $ k a
instance (Monad m) => Monad (MaybeT m)

instance FunctorUnit2 MaybeT where funit2 = MaybeT .^ Just
instance FunctorJoin2 MaybeT where
  fjoin2 = MaybeT . ff ^. unMaybeT . unMaybeT
    where
      ff Nothing = Nothing
      ff (Just aM) = aM
instance FunctorFunctor2 MaybeT where
  fmap2 :: (Functor m, Functor n) => (m ~> n) -> MaybeT m ~> MaybeT n
  fmap2 f = MaybeT . f . unMaybeT

instance (Functor m) => MonadMaybe (MaybeT m) where
  maybeI :: MaybeT m ~> MaybeT (MaybeT m)
  maybeI = maybeCommute . funit2
  maybeE :: MaybeT (MaybeT m) ~> MaybeT m
  maybeE = fjoin2 . maybeCommute

-- }}}

-- ErrorT {{{

mapError :: (Functor m) => (e1 -> e2) -> ErrorT e1 m a -> ErrorT e2 m a
mapError f = ErrorT . mapInl f ^. unErrorT

errorCommute :: (Functor m) => ErrorT e (ErrorT e m) ~> ErrorT e (ErrorT e m)
errorCommute = ErrorT . ErrorT . ff ^. unErrorT . unErrorT
  where
    ff (Inl e) = Inr (Inl e)
    ff (Inr (Inl e)) = Inl e
    ff (Inr (Inr a)) = Inr $ Inr a

instance (Unit m) => Unit (ErrorT e m) where
  unit a = ErrorT $ unit $ Inr a
instance (Functor m) => Functor (ErrorT e m) where
  map f aM = ErrorT $ mapInr f ^$ unErrorT aM
instance (Functor m, Product m) => Product (ErrorT e m) where
  aM1 <*> aM2 = ErrorT $ ff ^$ unErrorT aM1 <*> unErrorT aM2
    where
      ff (Inl e, _) = Inl e
      ff (_, Inl e) = Inl e
      ff (Inr a, Inr b) = Inr (a, b)
instance (Functor m, Applicative m) => Applicative (ErrorT e m) where
  fM <@> aM = ErrorT $ ff ^@ unErrorT fM <$> unErrorT aM
    where
      ff (Inl e) _ = Inl e
      ff _ (Inl e) = Inl e
      ff (Inr f) (Inr a) = Inr $ f a
instance (Unit m, Functor m, Bind m) => Bind (ErrorT e m) where
  aM >>= k = ErrorT $ do
    aeM <- unErrorT aM
    case aeM of
      Inl e -> unit $ Inl e
      Inr a -> unErrorT $ k a
instance (Monad m) => Monad (ErrorT e m) where
instance FunctorUnit2 (ErrorT e) where
  funit2 :: (Functor m) => m ~> ErrorT e m
  funit2 aM = ErrorT $ Inr ^$ aM
instance FunctorJoin2 (ErrorT e) where
  fjoin2 :: (Functor m) => ErrorT e (ErrorT e m) ~> ErrorT e m
  fjoin2 = ErrorT . ff ^. unErrorT . unErrorT
    where
      ff (Inl e) = Inl e
      ff (Inr ea) = ea
instance FunctorFunctor2 (ErrorT e) where
  fmap2 :: (Functor m, Functor n) => m ~> n -> ErrorT e m ~> ErrorT e n
  fmap2 f = ErrorT . f . unErrorT

instance (Functor m) => MonadError e (ErrorT e m) where
  errorI :: ErrorT e m ~> ErrorT e (ErrorT e m)
  errorI = errorCommute . funit2
  errorE :: ErrorT e (ErrorT e m) ~> ErrorT e m
  errorE = fjoin2 . errorCommute

-- }}}

-- ReaderT {{{

type Reader r = ReaderT r ID

runReader :: r -> Reader r a -> a
runReader r = unID . runReaderT r

readerCommute :: ReaderT r1 (ReaderT r2 m) ~> ReaderT r2 (ReaderT r1 m)
readerCommute aMM = ReaderT $ \ r2 -> ReaderT $ \ r1 -> runReaderT r2 $ runReaderT r1 aMM

instance (Unit m) => Unit (ReaderT r m) where
  unit = ReaderT . const . unit
instance (Functor m) => Functor (ReaderT r m) where
  map f = ReaderT . f ^^. unReaderT
instance (Product m) => Product (ReaderT r m) where
  aM1 <*> aM2 = ReaderT $ \ r ->
    runReaderT r aM1 <*> runReaderT r aM2
instance (Applicative m) => Applicative (ReaderT r m) where
  fM <@> aM = ReaderT $ \ r ->
    runReaderT r fM <@> runReaderT r aM
instance (Bind m) => Bind (ReaderT r m) where
  aM >>= k = ReaderT $ \ r -> runReaderT r . k *$ runReaderT r aM
instance (Monad m) => Monad (ReaderT r m) where
instance FunctorUnit2 (ReaderT r) where
  funit2 = ReaderT . const
instance FunctorJoin2 (ReaderT r) where
  fjoin2 aMM = ReaderT $ \ r -> runReaderT r $ runReaderT r aMM
instance FunctorFunctor2 (ReaderT r) where
  fmap2 :: (Functor m, Functor n) => (m ~> n) -> (ReaderT r m ~> ReaderT r n)
  fmap2 f aM = ReaderT $ \ r -> f $ runReaderT r aM

instance (Functor m) => MonadReader r (ReaderT r m) where
  readerI :: ReaderT r m ~> ReaderT r (ReaderT r m)
  readerI = readerCommute . funit2
  readerE :: ReaderT r (ReaderT r m) ~> ReaderT r m
  readerE = fjoin2 . readerCommute

instance (MonadBot m) => MonadBot (ReaderT r m) where
  mbot = ReaderT $ const mbot
instance (MonadAppend m) => MonadAppend (ReaderT r m) where
  aM1 <++> aM2 = ReaderT $ \ r -> unReaderT aM1 r <++> unReaderT aM2 r

-- }}}

-- WriterT {{{

execWriterT :: (Functor m) => WriterT o m a -> m o
execWriterT = fst ^. unWriterT

mapOutput :: (Functor m) => (o1 -> o2) -> WriterT o1 m a -> WriterT o2 m a
mapOutput f = WriterT . mapFst f ^. unWriterT

writerCommute :: (Functor m) => WriterT o1 (WriterT o2 m) ~> WriterT o2 (WriterT o1 m)
writerCommute aMM = WriterT $ WriterT $ ff ^$ unWriterT $ unWriterT aMM
  where
    ff (o2, (o1, a)) = (o1, (o2, a))

instance (Unit m, Monoid o) => Unit (WriterT o m) where unit = WriterT . unit . (null,)
instance (Functor m) => Functor (WriterT o m) where map f = WriterT . mapSnd f ^. unWriterT
instance (Functor m, Product m, Monoid o) => Product (WriterT o m) where
  aM1 <*> aM2 = WriterT $ ff ^$ unWriterT aM1 <*> unWriterT aM2
    where
      ff ((o1, a1), (o2, a2)) = (o1 ++ o2, (a1, a2))
instance (Functor m, Applicative m, Monoid o) => Applicative (WriterT o m) where
  fM <@> aM = WriterT $ ff2 ^$ ff1 ^@ unWriterT fM <$> unWriterT aM
    where
      ff1 (o, f) = mapSnd ((o,) . f)
      ff2 (o2, (o1, a)) = (o1 ++ o2, a)
instance (Monad m, Monoid o) => Bind (WriterT o m) where
  aM >>= k = WriterT $ do
    (o1, a) <- unWriterT aM
    (o2, b) <- unWriterT $ k a
    return (o1 ++ o2, b)
instance (Monad m, Monoid o) => Monad (WriterT o m) where
instance (Monoid w) => FunctorUnit2 (WriterT w) where
  funit2 = WriterT .^ (null,)
instance FunctorJoin2 (WriterT w) where
  fjoin2 = snd ^. unWriterT
instance FunctorFunctor2 (WriterT w) where
  fmap2 :: (Functor m, Functor n) => (m ~> n) -> (WriterT w m ~> WriterT w n)
  fmap2 f aM = WriterT $ f $ unWriterT aM

instance (Functor m, Monoid o) => MonadWriter o (WriterT o m) where
  writerI :: WriterT o m ~> WriterT o (WriterT o m)
  writerI = writerCommute . funit2
  writerE :: WriterT o (WriterT o m) ~> WriterT o m
  writerE = fjoin2 . writerCommute

instance (MonadBot m, Monoid o) => MonadBot (WriterT o m) where
  mbot = WriterT $ mbot

-- }}}

-- StateT {{{ --

runStateT :: s -> StateT s m a -> m (s, a)
runStateT = flip unStateT

evalStateT :: (Functor m) => s -> StateT s m a -> m a
evalStateT = snd ^.: runStateT

execStateT :: (Functor m) => s -> StateT s m a -> m s
execStateT = fst ^.: runStateT

mapStateT :: (Functor m) => (s1 -> s2) -> (s2 -> s1) -> StateT s1 m a -> StateT s2 m a
mapStateT to from aM = StateT $ \ s2 -> ff ^$ unStateT aM $ from s2
  where ff (s1, a) = (to s1, a)

type State s = StateT s ID

runState :: s -> State s a -> (s, a)
runState = unID .: runStateT

evalState :: s -> State s a -> a
evalState = snd .: runState

execState :: s -> State s a -> s
execState = fst .: runState

stateCommute :: (Functor m) => StateT s1 (StateT s2 m) ~> StateT s2 (StateT s1 m)
stateCommute aMM = StateT $ \ s2 -> StateT $ \ s1 -> ff ^$ runStateT s2 $ runStateT s1 aMM
  where
    ff (s2, (s1, a)) = (s1, (s2, a))

stateLens :: (Functor m) => Lens s1 s2 -> StateT s2 m ~> StateT s1 m
stateLens l aM = StateT $ \ s1 ->
  let s2 = access l s1
      ff (s2', a) = (set l s2' s1, a)
  in ff ^$ unStateT aM s2

stateLensE :: (MonadState s1 m, Monad m) => Lens s1 s2 -> (StateT s2 m ~> m)
stateLensE = stateE .: stateLens

instance (Unit m) => Unit (StateT s m) where unit x = StateT $ \ s -> unit (s, x)
instance (Functor m) => Functor (StateT s m) where map f aM = StateT $ \ s -> mapSnd f ^$ unStateT aM s
instance (Monad m) => Product (StateT s m) where (<*>) = mpair
instance (Monad m) => Applicative (StateT s m) where (<@>) = mapply
instance (Monad m) => Bind (StateT s m) where
  aM >>= k = StateT $ \ s -> do
    (s', a) <- unStateT aM s
    unStateT (k a) s'
instance (Monad m) => Monad (StateT s m) where
instance FunctorUnit2 (StateT s) where funit2 aM = StateT $ \ s -> (s,) ^$ aM
instance FunctorJoin2 (StateT s) where fjoin2 aMM = StateT $ \ s -> runStateT s $ snd ^$ runStateT s aMM
instance FunctorFunctor2 (StateT s) where
  fmap2 :: (Functor m, Functor n) => (m ~> n) -> StateT s m ~> StateT s n
  fmap2 f aM = StateT $ f . unStateT aM

instance (MonadBot m) => MonadBot (StateT s m) where mbot = StateT $ const mbot
instance (MonadTop m) => MonadTop (StateT s m) where mtop = StateT $ const mtop
instance (MonadPlus m) => MonadPlus (StateT s m) where
  aM1 <+> aM2 = StateT $ \ s -> unStateT aM1 s <+> unStateT aM2 s
instance (MonadAppend m) => MonadAppend (StateT s m) where
  aM1 <++> aM2 = StateT $ \ s -> unStateT aM1 s <++> unStateT aM2 s

instance (Functorial Monoid m, Monoid s, Monoid a) => Monoid (StateT s m a) where
  null =
    with (functorial :: W (Monoid (m (s, a)))) $
    StateT $ \ _ -> null
  aM1 ++ aM2 =
    with (functorial :: W (Monoid (m (s, a)))) $
    StateT $ \ s -> unStateT aM1 s ++ unStateT aM2 s
instance (Functorial Monoid m, Monoid s) => Functorial Monoid (StateT s m) where
  functorial = W
instance (Functorial Bot m, Bot s, Bot a) => Bot (StateT s m a) where
  bot :: StateT s m a
  bot = 
    with (functorial :: W (Bot (m (s, a)))) $
    StateT $ \ _ -> bot
instance (Functorial Join m, Join s, Join a) => Join (StateT s m a) where
  aM1 \/ aM2 = 
    with (functorial :: W (Join (m (s, a)))) $
    StateT $ \ s -> unStateT aM1 s \/ unStateT aM2 s
instance (Functorial Bot m, Functorial Join m, JoinLattice s, JoinLattice a) => JoinLattice (StateT s m a)
instance (Functorial Bot m, Functorial Join m, JoinLattice s) => Functorial JoinLattice (StateT s m) where functorial = W

instance (Functor m) => MonadState s (StateT s m) where
  stateI :: StateT s m ~> StateT s (StateT s m) 
  stateI = stateCommute . funit2
  stateE :: StateT s (StateT s m) ~> StateT s m
  stateE = fjoin2 . stateCommute

-- }}} --

-- AddStateT {{{

newtype AddStateT s12 s1 m a = AddStateT { runAddStateT :: StateT s1 m a }
  deriving 
    ( Unit, Functor, Product, Applicative, Bind, Monad
    , MonadBot, MonadPlus, MonadAppend
    , MonadReader r
    , MonadWriter o
    )

mergeState :: (Functor m) => StateT s1 (StateT s2 m) a -> StateT (s1, s2) m a
mergeState aMM = StateT $ \ (s1, s2) -> ff ^$ unStateT (unStateT aMM s1) s2
  where
    ff (s2, (s1, a)) = ((s1, s2), a)

splitState :: (Functor m) => StateT (s1, s2) m a -> StateT s1 (StateT s2 m) a
splitState aM = StateT $ \ s1 -> StateT $ \ s2 -> ff ^$ unStateT aM (s1, s2)
  where
    ff ((s1, s2), a) = (s2, (s1, a))

instance (Functor m, MonadState s2 m, Isomorphism s12 (s1, s2)) => MonadState s12 (AddStateT s12 s1 m) where
  stateI :: AddStateT s12 s1 m ~> StateT s12 (AddStateT s12 s1 m)
  stateI = 
    fmap2 AddStateT
    . mapStateT isofrom isoto
    . mergeState
    . fmap2 (stateCommute . fmap2 stateI)
    . stateI
    . runAddStateT
  stateE :: StateT s12 (AddStateT s12 s1 m) ~> AddStateT s12 s1 m
  stateE = 
    AddStateT
    . stateE
    . fmap2 (fmap2 stateE . stateCommute) 
    . splitState
    . mapStateT isoto isofrom
    . fmap2 runAddStateT

-- }}}

-- RWST {{{

runRWST :: (Functor m) => r -> s -> RWST r o s m a -> m (s, o, a)
runRWST r0 s0 = ff ^. runStateT s0 . unWriterT . runReaderT r0 . unRWST
  where
    ff (s, (o, a)) = (s, o, a)
rwsCommute :: (Monad m, Monoid o1, Monoid o2) => RWST r1 o1 s1 (RWST r2 o2 s2 m) ~> RWST r2 o2 s2 (RWST r1 o1 s1 m)
rwsCommute =
  RWST
  . fmap2 (fmap2 rwsStateCommute . rwsWriterCommute)
  . rwsReaderCommute
  . mmap2 unRWST

deriving instance (Unit m, Monoid o) => Unit (RWST r o s m)
deriving instance (Functor m) => Functor (RWST r o s m)
deriving instance (Monad m, Monoid o) => Product (RWST r o s m)
deriving instance (Monad m, Monoid o) => Applicative (RWST r o s m)
deriving instance (Monad m, Monoid o) => Bind (RWST r o s m)
deriving instance (Monad m, Monoid o) => Monad (RWST r o s m)
instance (Monoid o) => MonadUnit2 (RWST r o s) where
  munit2 = RWST . funit2 . funit2 . funit2
instance (Monoid o) => MonadJoin2 (RWST r o s) where
  mjoin2 =
    RWST
    . fjoin2
    . fmap2 (fmap2 fjoin2 . writerReaderCommute)
    . fmap2 (fmap2 (fmap2 (fmap2 fjoin2 . stateWriterCommute) . stateReaderCommute))
    . unRWST . mmap2 unRWST
instance (Monoid o) => MonadFunctor2 (RWST r o s) where
  mmap2 f = RWST . fmap2 (fmap2 (fmap2 f)) . unRWST

deriving instance (Monad m, Monoid o) => MonadReader r (RWST r o s m)
deriving instance (Monad m, Monoid o) => MonadWriter o (RWST r o s m)
deriving instance (Monad m, Monoid o) => MonadState s (RWST r o s m)
instance (Monad m, Monoid o) => MonadRWS r o s (RWST r o s m) where
  rwsI :: RWST r o s m ~> RWST r o s (RWST r o s m)
  rwsI = rwsCommute . munit2
  rwsE :: RWST r o s (RWST r o s m) ~> RWST r o s m
  rwsE = mjoin2 . rwsCommute

deriving instance (MonadBot m, Monoid o) => MonadBot (RWST r o s m)
deriving instance (Functor m, MonadMaybe m, Monoid o) => MonadMaybe (RWST r o s m)

-- }}}

-- ListT {{{

newtype ListT m a = ListT { unListT :: m [a] }
listCommute :: (Functor m) => ListT (ListT m) ~> ListT (ListT m)
listCommute = ListT . ListT . transpose ^. unListT . unListT

instance (Unit m) => Unit (ListT m) where
  unit = ListT . unit . single
instance (Functor m) => Functor (ListT m) where
  map f = ListT . f ^^. unListT
instance (Monad m, Functorial Monoid m) => Product (ListT m) where
  (<*>) = mpair
instance (Monad m, Functorial Monoid m) => Applicative (ListT m) where
  (<@>) = mapply
instance (Monad m, Functorial Monoid m) => Bind (ListT m) where
  (>>=) :: forall a b. ListT m a -> (a -> ListT m b) -> ListT m b
  aM >>= k = ListT $ do
    xs <- unListT aM
    unListT $ concat $ k ^$ xs
instance (Monad m, Functorial Monoid m) => Monad (ListT m) where
instance FunctorUnit2 ListT where
  funit2 = ListT .^ unit
instance FunctorJoin2 ListT where
  fjoin2 = ListT . concat ^. unListT . unListT
instance FunctorFunctor2 ListT where
  fmap2 f = ListT . f . unListT

instance (Functorial Monoid m) => Monoid (ListT m a) where
  null = 
    with (functorial :: W (Monoid (m [a]))) $
    ListT null
  xs ++ ys = 
    with (functorial :: W (Monoid (m [a]))) $
    ListT $ unListT xs ++ unListT ys
instance (Functorial Monoid m) => Functorial Monoid (ListT m) where functorial = W
instance (Functorial Monoid m) => MonadBot (ListT m) where
  mbot =  null
instance (Functorial Monoid m) => MonadAppend (ListT m) where
  (<++>) = (++)

maybeToList :: (Functor m) => MaybeT m a -> ListT m a
maybeToList aM = ListT $ ff ^$ unMaybeT aM
  where
    ff Nothing = []
    ff (Just a) = [a]

-- }}}

-- ListSetT {{{

newtype ListSetT m a = ListSetT { unListSetT :: m (ListSet a) }
listSetCommute :: (Functor m) => ListSetT (ListSetT m) ~> ListSetT (ListSetT m)
listSetCommute = ListSetT . ListSetT . listSetTranspose ^. unListSetT . unListSetT

instance (Unit m) => Unit (ListSetT m) where
  unit = ListSetT . unit . ListSet . single
instance (Functor m) => Functor (ListSetT m) where
  map f = ListSetT . f ^^. unListSetT
instance (Monad m, Functorial JoinLattice m) => Product (ListSetT m) where
  (<*>) = mpair
instance (Monad m, Functorial JoinLattice m) => Applicative (ListSetT m) where
  (<@>) = mapply
instance (Monad m, Functorial JoinLattice m) => Bind (ListSetT m) where
  (>>=) :: forall a b. ListSetT m a -> (a -> ListSetT m b) -> ListSetT m b
  aM >>= k = ListSetT $ do
    xs <- unListSetT aM
    unListSetT $ msum $ k ^$ xs
-- PROOF of: monad laws for (ListSetT m) {{{
--
-- ASSUMPTION 1: returnₘ a <+> returnₘ b = returnₘ (a \/ b)
-- [this comes from m being a lattice functor. (1 x + 1 y) = 1 (x + y)]
--
-- * PROOF of: left unit := return x >>= k = k x {{{
--   
--   return x >>= k
--   = [[definition of >>=]]
--   ListSetT $ do { xs <- unListSetT $ return x ; unListSetT $ msums $ map k xs }
--   = [[definition of return]]
--   ListSetT $ do { xs <- unListSetT $ ListSetT $ return [x] ; unListSetT $ msums $ map k xs }
--   = [[ListSetT beta]]
--   ListSetT $ do { xs <- return [x] ; unListSetT $ msums $ map k xs }
--   = [[monad left unit]]
--   ListSetT $ unListSetT $ msums $ map k [x]
--   = [[definition of map]]
--   ListSetT $ unListSetT $ msums $ [k x]
--   = [[definition of msums and <+> unit]]
--   ListSetT $ unListSetT $ k x
--   = [[ListSetT eta]]
--   k x
--   QED }}}
--
-- * PROOF of: right unit := aM >>= return = aM {{{
--
--   aM >>= return
--   = [[definition of >>=]]
--   ListSetT $ { xs <- unListSetT aM ; unListSetT $ msums $ map return xs }
--   = [[induction/expansion on xs]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; unListSetT $ msums $ map return [x1,..,xn] }
--   = [[definition of return and map]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; unListSetT $ msums $ [ListSetT $ return [x1],..,ListSetT $ return [xn]] }
--   = [[definition of msums]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; unListSetT $ ListSetT $ return [x1] <+> .. <+> return [xn] }
--   = [[assumption 1]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; unListSetT $ ListSetT $ return [x1,..,xn] }
--   = [[ListSetT beta]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; return [x1,..,xn] }
--   = [[monad right unit]]
--   ListSetT $ unListSetT aM
--   = [[ListSetT eta]]
--   aM
--   QED }}}
--
-- * PROOF of: associativity := (aM >>= k1) >>= k2 = { x <- aM ; k1 x >>= k2 } {{{
--
--   (aM >>= k1) >>= k2
--   = [[definition of >>=]]
--   ListSetT $ { xs <- unListSetT $ ListSetT $ { xs' <- unListSetT aM ; unListSetT $ msums $ map k1 xs' } ; unListSetT $ msums $ map k xs }
--   = [[ListSetT beta]]
--   ListSetT $ { xs <- { xs' <- unListSetT aM ; unListSetT $ msums $ map k1 xs' } ; unListSetT $ msums $ map k xs }
--   = [[monad associativity]]
--   ListSetT $ { xs' <- unListSetT aM ; xs <- unListSetT $ msums $ map k1 xs' ; unListSetT $ msums $ map k xs }
--   =
--   LHS
--
--   { x <- aM ; k1 x >>= k2 }
--   = [[definition of >>=]]
--   ListSetT $ { xs' <- unListSetT aM ; unListSetT $ msums $ map (\ x -> ListSetT $ { xs <- unListSetT (k1 x) ; unListSetT $ msums $ map k2 xs }) xs' }
--   = [[induction/expansion on xs']]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; unListSetT $ msums $ map (\ x -> ListSetT $ { xs <- unListSetT (k1 x) ; unListSetT $ msums $ map k2 xs }) [x1,..,xn] }
--   = [[definition of map]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; unListSetT $ msums $ [ListSetT $ { xs <- unListSetT (k1 x1) ; unListSetT $ msums $ map k2 xs },..,ListSetT $ { xs <- unListSetT (k1 xn) ; runList $ msums $ map k2 xs}] }
--   = [[definition of msum]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; unListSetT $ ListSetT { xs <- unListSetT (k1 x1) ; unListSetT $ msums $ map k2 xs } <+> .. <+> ListSetT { xs <- unListSetT (k1 xn) ; unListSetT $ msums $ map k2 xs } }
--   = [[ListSetT beta and definition of <+> for ListSetT]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; { xs <- unListSetT (k1 x1) ; unListSetT $ msums $ map k2 xs } <+> .. <+> { xs <- unListSetT (k1 xn) ; unListSetT $ msums $ map k2 xs } }
--   = [[<+> distribute with >>=]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; xs <- (unListSetT (k1 x1) <+> .. <+> unListSetT (k1 xn)) ;  unListSetT $ msums $ map k2 xs }
--   = [[definition of msums and map]]
--   ListSetT $ { [x1,..,xn] <- unListSetT aM ; xs <- unListSetT $ msums $ map k1 [x1,..,xn] ; unListSetT $ msums $ map k2 xs }
--   = [[collapsing [x1,..,xn]]]
--   ListSetT $ { xs' <- unListSetT aM ; xs <- unListSetT $ msums $ map k1 xs' ; unListSetT $ msums $ map k xs }
--   =
--   RHS
--
--   LHS = RHS
--   QED }}}
--
-- }}}
instance (Monad m, Functorial JoinLattice m) => Monad (ListSetT m) where
instance FunctorUnit2 ListSetT where
  funit2 = ListSetT .^ unit
instance FunctorJoin2 ListSetT where
  fjoin2 = ListSetT . concat ^. unListSetT . unListSetT
instance FunctorFunctor2 ListSetT where
  fmap2 f = ListSetT . f . unListSetT

instance (Functorial JoinLattice m) => MonadBot (ListSetT m) where
  mbot :: forall a. ListSetT m a
  mbot = 
    with (functorial :: W (JoinLattice (m (ListSet a)))) $
    ListSetT bot
instance (Functorial JoinLattice m) => MonadPlus (ListSetT m) where
  (<+>) :: forall a. ListSetT m a -> ListSetT m a -> ListSetT m a
  aM1 <+> aM2 = 
    with (functorial :: W (JoinLattice (m (ListSet a)))) $
    ListSetT $ unListSetT aM1 \/ unListSetT aM2

-- }}}

-- ListSetWithTopT {{{

newtype ListSetWithTopT m a = ListSetWithTopT { unListSetWithTopT :: m (ListSetWithTop a) }
listSetWithTopCommute :: (Functor m) => ListSetWithTopT (ListSetWithTopT m) ~> ListSetWithTopT (ListSetWithTopT m)
listSetWithTopCommute = ListSetWithTopT . ListSetWithTopT . listSetWithTopTranspose ^. unListSetWithTopT . unListSetWithTopT

instance (Unit m) => Unit (ListSetWithTopT m) where unit = ListSetWithTopT . unit . single
instance (Functor m) => Functor (ListSetWithTopT m) where map f = ListSetWithTopT . f ^^. unListSetWithTopT
instance (Monad m, Functorial JoinLattice m, Functorial Top m) => Product (ListSetWithTopT m) where (<*>) = mpair
instance (Monad m, Functorial JoinLattice m, Functorial Top m) => Applicative (ListSetWithTopT m) where (<@>) = mapply
instance (Monad m, Functorial JoinLattice m, Functorial Top m) => Bind (ListSetWithTopT m) where
  (>>=) :: forall a b. ListSetWithTopT m a -> (a -> ListSetWithTopT m b) -> ListSetWithTopT m b
  aM >>= k = ListSetWithTopT $ do
    xs <- unListSetWithTopT aM
    unListSetWithTopT $ listSetWithTopElim mtop msum $ k ^$ xs
instance (Monad m, Functorial JoinLattice m, Functorial Top m) => Monad (ListSetWithTopT m) where
instance FunctorUnit2 ListSetWithTopT where
  funit2 = ListSetWithTopT .^ unit
instance FunctorJoin2 ListSetWithTopT where
  fjoin2 = ListSetWithTopT . listSetWithTopElim ListSetTop concat ^. unListSetWithTopT . unListSetWithTopT
instance FunctorFunctor2 ListSetWithTopT where
  fmap2 f = ListSetWithTopT . f . unListSetWithTopT

instance (Functorial JoinLattice m) => MonadBot (ListSetWithTopT m) where
  mbot :: forall a. ListSetWithTopT m a
  mbot = 
    with (functorial :: W (JoinLattice (m (ListSetWithTop a)))) $
    ListSetWithTopT bot
instance (Functorial Top m) => MonadTop (ListSetWithTopT m) where 
  mtop :: forall a. ListSetWithTopT m a
  mtop = 
    with (functorial :: W (Top (m (ListSetWithTop a)))) $
    ListSetWithTopT top
instance (Functorial JoinLattice m) => MonadPlus (ListSetWithTopT m) where
  (<+>) :: forall a. ListSetWithTopT m a -> ListSetWithTopT m a -> ListSetWithTopT m a
  aM1 <+> aM2 = 
    with (functorial :: W (JoinLattice (m (ListSetWithTop a)))) $
    ListSetWithTopT $ unListSetWithTopT aM1 \/ unListSetWithTopT aM2

-- }}}

-- SetT {{{

newtype SetT m a = SetT { unSetT :: m (Set a) }
mapSetT :: (m (Set a) -> m (Set b)) -> SetT m a -> SetT m b
mapSetT f = SetT . f . unSetT

setCommute :: (Functor m) => SetT (SetT m) ~> SetT (SetT m)
setCommute = SetT . SetT . setTranspose ^. unSetT . unSetT

instance (Functor m, Product m) => Product (SetT m) where
  (<*>) :: forall a b. SetT m a -> SetT m b -> SetT m (a, b)
  aM1 <*> aM2 = SetT $ uncurry (<*>) ^$ unSetT aM1 <*> unSetT aM2
instance (Functorial JoinLattice m, Monad m) => Bind (SetT m) where
  aM >>= k = SetT $ do
    aC <- unSetT aM
    unSetT $ msum $ k ^$ toList aC
instance (Functorial JoinLattice m) => MonadBot (SetT m) where
  mbot :: forall a. SetT m a
  mbot = 
    with (functorial :: W (JoinLattice (m (Set a)))) $
    SetT bot
instance (Functorial JoinLattice m) => MonadPlus (SetT m) where
  (<+>) :: forall a. SetT m a -> SetT m a -> SetT m a
  aM1 <+> aM2 =
    with (functorial :: W (JoinLattice (m (Set a)))) $
    SetT $ unSetT aM1 \/ unSetT aM2

-- }}}

-- ContT {{{

evalKonT :: (Unit m) => ContT r m r -> m r
evalKonT aM = unContT aM unit

type Kon r = ContT r ID
runKon :: Kon r a -> (a -> r) -> r
runKon aM f = unID $ unContT aM (ID . f)
evalKon :: Kon r r -> r
evalKon aM = runKon aM id

instance (Unit m) => Unit (ContT r m) where
  unit a = ContT $ \ k -> k a
instance (Unit m) => Applicative (ContT r m) where
  (<@>) = mapply
instance (Unit m) => Product (ContT r m) where
  (<*>) = mpair
instance (Unit m) => Functor (ContT r m) where
  map = mmap
instance (Unit m) => Bind (ContT r m) where
  (>>=) :: ContT r m a -> (a -> ContT r m b) -> ContT r m b
  aM >>= kM = ContT $ \ (k :: b -> m r) -> unContT aM $ \ a -> unContT (kM a) k
instance (Unit m) => Monad (ContT r m) where
instance MonadIsoFunctor2 (ContT r) where
  misoMap2 :: (Monad m, Monad n) => (m ~> n, n ~> m) -> (ContT r m ~> ContT r n)
  misoMap2 (to, from) aM = ContT $ \ (k :: a -> n r) -> to $ unContT aM $ \ a -> from $ k a

instance (Monad m) => MonadCont r (ContT r m) where
  contI :: ContT r m ~> ContT r (ContT r m)
  contI aM = ContT $ \ (k1 :: a -> ContT r m r) -> ContT $ \ (k2 :: r -> m r) ->
    k2 *$ unContT aM $ \ a -> unContT (k1 a) return
  contE :: ContT r (ContT r m) ~> ContT r m
  contE aMM = ContT $ \ (k :: a -> m r) -> 
    let aM :: ContT r m r
        aM = unContT aMM $ \ a -> ContT $ \ (k' :: r -> m r) -> k' *$ k a
    in unContT aM return

-- }}}

-- OpaqueContT {{{

newtype ContFun r m a = ContFun { unContFun :: a -> m r }
type OpaqueKon k r = OpaqueContT k r ID

runOpaqueKonTWith :: k r m a -> OpaqueContT k r m a -> m r
runOpaqueKonTWith = flip unOpaqueContT
makeMetaKonT :: (Morphism3 (k r) (ContFun r)) => ((a -> m r) -> m r) -> OpaqueContT k r m a
makeMetaKonT nk = OpaqueContT $ \ (k :: k r m a) -> nk $ unContFun $ morph3 k
runMetaKonT :: (Morphism3 (ContFun r) (k r)) => OpaqueContT k r m a -> (a -> m r) -> m r
runMetaKonT aM k = unOpaqueContT aM $ morph3 $ ContFun k
runMetaKonTWith :: (Morphism3 (ContFun r) (k r)) => (a -> m r) -> OpaqueContT k r m a -> m r
runMetaKonTWith = flip runMetaKonT
evalOpaqueKonT :: (Unit m, Morphism3 (ContFun r) (k r)) => OpaqueContT k r m r -> m r
evalOpaqueKonT aM = runMetaKonT aM unit

makeOpaqueKon :: (k r ID a -> r) -> OpaqueKon k r a
makeOpaqueKon nk = OpaqueContT $ ID . nk
makeMetaKon :: (Morphism3 (k r) (ContFun r)) => ((a -> r) -> r) -> OpaqueKon k r a
makeMetaKon nk = makeOpaqueKon $ \ (k :: k r ID a) -> nk $ (.) unID . unContFun $ morph3 k
runOpaqueKon :: OpaqueKon k r a -> k r ID a -> r
runOpaqueKon = unID .: unOpaqueContT
runMetaKon :: (Morphism3 (ContFun r) (k r)) => OpaqueKon k r a -> (a -> r) -> r
runMetaKon aM k = runOpaqueKon aM $ morph3 $ ContFun $ ID . k
evalOpaqueKon :: (Morphism3 (ContFun r) (k r)) => OpaqueKon k r r -> r
evalOpaqueKon aM = runMetaKon aM id

metaKonT :: (Morphism3 (ContFun r) (k r)) => OpaqueContT k r m ~> ContT r m
metaKonT aM = ContT $ \ (k :: a -> m r) -> runMetaKonT aM k

opaqueContT :: (Morphism3 (k r) (ContFun r)) => ContT r m ~> OpaqueContT k r m
opaqueContT aM = makeMetaKonT $ \ (k :: a -> m r) -> unContT aM k

instance (Morphism3 (k r) (ContFun r)) => Unit (OpaqueContT k r m) where
  unit :: a -> OpaqueContT k r m a
  unit a = OpaqueContT  $ \ k -> unContFun (morph3 k) a
instance (Monad m, Isomorphism3 (ContFun r) (k r)) => Functor (OpaqueContT k r m) where map = mmap
instance (Monad m, Isomorphism3 (ContFun r) (k r)) => Applicative (OpaqueContT k r m) where (<@>) = mapply
instance (Monad m, Isomorphism3 (ContFun r) (k r)) => Product (OpaqueContT k r m) where (<*>) = mpair
instance (Monad m, Isomorphism3 (ContFun r) (k r)) => Bind (OpaqueContT k r m) where
  (>>=) :: OpaqueContT k r m a -> (a -> OpaqueContT k r m b) -> OpaqueContT k r m b
  aM >>= kM = OpaqueContT $ \ (k :: k r m a) -> runMetaKonT aM $ \ a -> unOpaqueContT (kM a) k
instance (Monad m, Isomorphism3 (ContFun r) (k r)) => Monad (OpaqueContT k r m) where
instance (Isomorphism3 (k r) (ContFun r)) => MonadIsoFunctor2 (OpaqueContT k r) where
  misoMap2 :: (Monad m, Monad n) => (m ~> n, n ~> m) -> OpaqueContT k r m ~> OpaqueContT k r n
  misoMap2 tofrom = opaqueContT . misoMap2 tofrom . metaKonT

class Balloon k r | k -> r where
  inflate :: (Monad m) => k r m ~> k r (OpaqueContT k r m)
  deflate :: (Monad m) => k r (OpaqueContT k r m) ~> k r m
instance (Monad m, Isomorphism3 (ContFun r) (k r), Balloon k r) => MonadOpaqueCont k r (OpaqueContT k r m) where
  opaqueContI :: OpaqueContT k r m a -> OpaqueContT k r (OpaqueContT k r m) a
  opaqueContI aM = OpaqueContT $ \ k1 -> makeMetaKonT $ \ (k2 :: r -> m r) -> k2 *$ unOpaqueContT aM $ deflate k1
  opaqueContE :: OpaqueContT k r (OpaqueContT k r m) a -> OpaqueContT k r m a
  opaqueContE kk = OpaqueContT $ \ (k :: k r m a ) -> runMetaKonTWith return $ unOpaqueContT kk $ inflate k

instance (Monad m, Isomorphism3 (ContFun r) (k r)) => MonadCont r (OpaqueContT k r m) where
  contI :: OpaqueContT k r m ~> ContT r (OpaqueContT k r m)
  contI aM = ContT $ \ (k1 :: a -> OpaqueContT k r m r) -> makeMetaKonT $ \ (k2 :: r -> m r) ->
    k2 *$ runMetaKonT aM $ \ a -> runMetaKonT (k1 a) return
  contE :: ContT r (OpaqueContT k r m) ~> OpaqueContT k r m
  contE aMM = makeMetaKonT $ \ (k :: a -> m r) ->
    runMetaKonTWith return $ unContT aMM $ \ a -> makeMetaKonT $ \ (k' :: r -> m r) -> k' *$ k a

-- }}}

----------------------
-- Monads Commuting --
----------------------

-- Maybe // * --

-- Maybe // Reader // FULL COMMUTE {{{

maybeReaderCommute :: (Functor m) => MaybeT (ReaderT r m) ~> ReaderT r (MaybeT m)
maybeReaderCommute aMRM = ReaderT $ \ r -> MaybeT $ runReaderT r $ unMaybeT aMRM

readerMaybeCommute :: (Functor m) => ReaderT r (MaybeT m) ~> MaybeT (ReaderT r m)
readerMaybeCommute aRMM = MaybeT $ ReaderT $ \ r -> unMaybeT $ runReaderT r aRMM

instance (Functor m, MonadReader r m) => MonadReader r (MaybeT m) where
  readerI :: MaybeT m ~> ReaderT r (MaybeT m)
  readerI = maybeReaderCommute . fmap2 readerI
  readerE :: ReaderT r (MaybeT m) ~> MaybeT m
  readerE = fmap2 readerE . readerMaybeCommute

instance (Functor m, MonadMaybe m) => MonadMaybe (ReaderT r m) where
  maybeI :: ReaderT r m ~> MaybeT (ReaderT r m)
  maybeI = readerMaybeCommute . fmap2 maybeI
  maybeE :: MaybeT (ReaderT r m) ~> ReaderT r m
  maybeE = fmap2 maybeE . maybeReaderCommute

-- }}}

-- Maybe // Writer {{{

writerMaybeCommute :: (Monoid w, Functor m) => WriterT w (MaybeT m) ~> MaybeT (WriterT w m)
writerMaybeCommute aRMM = MaybeT $ WriterT $ ff ^$ unMaybeT $ unWriterT aRMM
  where
    ff Nothing = (null, Nothing)
    ff (Just (w, a)) = (w, Just a)

maybeWriterCommute :: (Functor m) => MaybeT (WriterT w m) ~> WriterT w (MaybeT m)
maybeWriterCommute aMRM = WriterT $ MaybeT $ ff ^$ unWriterT $ unMaybeT aMRM
  where
    ff (_, Nothing) = Nothing
    ff (w, Just a) = Just (w, a)

instance (Monoid w, Functor m, MonadWriter w m) => MonadWriter w (MaybeT m) where
  writerI :: MaybeT m ~> WriterT w (MaybeT m)
  writerI = maybeWriterCommute . fmap2 writerI
  writerE :: WriterT w (MaybeT m) ~> MaybeT m
  writerE = fmap2 writerE . writerMaybeCommute

instance (Monoid w, Functor m, MonadMaybe m) => MonadMaybe (WriterT w m) where
  maybeI :: WriterT w m ~> MaybeT (WriterT w m)
  maybeI = writerMaybeCommute . fmap2 maybeI
  maybeE :: MaybeT (WriterT w m) ~> WriterT w m
  maybeE = fmap2 maybeE . maybeWriterCommute

-- }}}

-- Maybe // State {{{

maybeStateCommute :: (Functor m) => MaybeT (StateT s m) ~> StateT s (MaybeT m)
maybeStateCommute aMSM = StateT $ \ s1 -> MaybeT $ ff ^$ runStateT s1 $ unMaybeT aMSM
  where
    ff (_, Nothing) = Nothing
    ff (s2, Just a) = Just (s2, a)

stateMaybeCommute :: (Functor m) => StateT s (MaybeT m) ~> MaybeT (StateT s m)
stateMaybeCommute aSMM = MaybeT $ StateT $ \ s1 -> ff s1 ^$ unMaybeT $ runStateT s1 aSMM
  where
    ff s1 Nothing = (s1, Nothing)
    ff _ (Just (s2, a)) = (s2, Just a)

instance (Functor m, MonadState s m) => MonadState s (MaybeT m) where
  stateI :: MaybeT m ~> StateT s (MaybeT m)
  stateI = maybeStateCommute . fmap2 stateI
  stateE :: StateT s (MaybeT m) ~> MaybeT m
  stateE = fmap2 stateE . stateMaybeCommute

instance (Functor m, MonadMaybe m) => MonadMaybe (StateT s m) where
  maybeI :: StateT s m ~> MaybeT (StateT s m)
  maybeI = stateMaybeCommute . fmap2 maybeI
  maybeE :: MaybeT (StateT s m) ~> StateT s m
  maybeE = fmap2 maybeE . maybeStateCommute

-- }}}

-- Error // * --

-- Error // Reader {{{

errorReaderCommute :: ErrorT e (ReaderT r m) ~> ReaderT r (ErrorT e m)
errorReaderCommute aMRM = ReaderT $ \ r -> ErrorT $ runReaderT r $ unErrorT aMRM

readerErrorCommute :: ReaderT r (ErrorT e m) ~> ErrorT e (ReaderT r m)
readerErrorCommute aRMM = ErrorT $ ReaderT $ \ r -> unErrorT $ runReaderT r aRMM

instance (Functor m, MonadReader r m) => MonadReader r (ErrorT e m) where
  readerI :: ErrorT e m ~> ReaderT r (ErrorT e m)
  readerI = errorReaderCommute . fmap2 readerI
  readerE :: ReaderT r (ErrorT e m) ~> ErrorT e m
  readerE = fmap2 readerE . readerErrorCommute

instance (Functor m, MonadError e m) => MonadError e (ReaderT r m) where
  errorI :: ReaderT r m ~> ErrorT e (ReaderT r m)
  errorI = readerErrorCommute . fmap2 errorI
  errorE :: ErrorT e (ReaderT r m) ~> ReaderT r m
  errorE = fmap2 errorE . errorReaderCommute

-- }}}

-- Error // State {{{

errorStateCommute :: (Functor m) => ErrorT e (StateT s m) ~> StateT s (ErrorT e m)
errorStateCommute aMRM = StateT $ \ s -> ErrorT $ ff ^$ runStateT s $ unErrorT aMRM
  where
    ff (_, Inl e) = Inl e
    ff (s, Inr a) = Inr (s, a)

stateErrorCommute :: (Functor m) => StateT s (ErrorT e m) ~> ErrorT e (StateT s m)
stateErrorCommute aRMM = ErrorT $ StateT $ \ s -> ff s ^$ unErrorT $ runStateT s aRMM
  where
    ff s (Inl e) = (s, Inl e)
    ff _ (Inr (s, a)) = (s, Inr a)

instance (Functor m, MonadState s m) => MonadState s (ErrorT e m) where
  stateI :: ErrorT e m ~> StateT s (ErrorT e m)
  stateI = errorStateCommute . fmap2 stateI
  stateE :: StateT s (ErrorT e m) ~> ErrorT e m
  stateE = fmap2 stateE . stateErrorCommute

instance (Functor m, MonadError e m) => MonadError e (StateT s m) where
  errorI :: StateT s m ~> ErrorT e (StateT s m)
  errorI = stateErrorCommute . fmap2 errorI
  errorE :: ErrorT e (StateT s m) ~> StateT s m
  errorE = fmap2 errorE . errorStateCommute

-- }}}

-- Reader // * --

-- Reader // Writer // FULL COMMUTE {{{

readerWriterCommute :: ReaderT r (WriterT w m) ~> WriterT w (ReaderT r m)
readerWriterCommute aRWM = WriterT $ ReaderT $ \ r -> unWriterT $ runReaderT r aRWM

writerReaderCommute :: WriterT w (ReaderT r m) ~> ReaderT r (WriterT w m)
writerReaderCommute aWRM = ReaderT $ \ r -> WriterT $ runReaderT r $ unWriterT aWRM

instance (Monoid w, Functor m, MonadWriter w m) => MonadWriter w (ReaderT r m) where
  writerI :: ReaderT r m ~> WriterT w (ReaderT r m)
  writerI = readerWriterCommute . fmap2 writerI
  writerE :: WriterT w (ReaderT r m) ~> ReaderT r m
  writerE = fmap2 writerE . writerReaderCommute

instance (Monoid w, Functor m, MonadReader r m) => MonadReader r (WriterT w m) where
  readerI :: WriterT w m ~> ReaderT r (WriterT w m)
  readerI = writerReaderCommute . fmap2 readerI
  readerE :: ReaderT r (WriterT w m) ~> WriterT w m
  readerE = fmap2 readerE . readerWriterCommute

-- }}}

-- Reader // State // FULL COMMUTE {{{

readerStateCommute :: (Functor m) => ReaderT r (StateT s m) ~> StateT s (ReaderT r m)
readerStateCommute aRSM = StateT $ \ s -> ReaderT $ \ r ->
  runStateT s $ runReaderT r aRSM

stateReaderCommute :: (Functor m) => StateT s (ReaderT r m) ~> ReaderT r (StateT s m)
stateReaderCommute aSRM = ReaderT $ \ r -> StateT $ \ s -> 
  runReaderT r $ runStateT s aSRM

instance (Functor m, MonadState s m) => MonadState s (ReaderT r m) where
  stateI :: ReaderT r m ~> StateT s (ReaderT r m)
  stateI = readerStateCommute . fmap2 stateI
  stateE :: StateT s (ReaderT r m) ~> ReaderT r m
  stateE = fmap2 stateE . stateReaderCommute

instance (Functor m, MonadReader r m) => MonadReader r (StateT s m) where
  readerI :: StateT s m ~> ReaderT r (StateT s m)
  readerI = stateReaderCommute . fmap2 readerI
  readerE :: ReaderT r (StateT s m) ~> StateT s m
  readerE = fmap2 readerE . readerStateCommute

-- }}}

-- Reader // RWS {{{

readerRWSCommute :: (Monad m, Monoid o) => ReaderT r1 (RWST r2 o s m) ~> RWST r2 o s (ReaderT r1 m)
readerRWSCommute =
    RWST
  . fmap2 
    ( fmap2 readerStateCommute
    . readerWriterCommute
    )
  . readerCommute
  . fmap2 unRWST

rwsReaderCommute :: (Monad m, Monoid o) => RWST r1 o s (ReaderT r2 m) ~> ReaderT r2 (RWST r1 o s m)
rwsReaderCommute = 
    fmap2 RWST
  . readerCommute
  . fmap2 
    ( writerReaderCommute
    . fmap2 stateReaderCommute
    )
  . unRWST
       
-- }}}

-- Writer // * --

-- Writer // State // FULL COMMUTE {{{

writerStateCommute :: (Functor m) => WriterT w (StateT s m) ~> StateT s (WriterT w m)
writerStateCommute aRMM = StateT $ \ s1 -> WriterT $ ff ^$ runStateT s1 $ unWriterT aRMM
  where
    ff (s, (w, a)) = (w, (s, a))

stateWriterCommute :: (Functor m) => StateT s (WriterT w m) ~> WriterT w (StateT s m)
stateWriterCommute aMRM = WriterT $ StateT $ ff ^. unWriterT . unStateT aMRM
  where
    ff (w, (s, a)) = (s, (w, a))

instance (Functor m, Monoid w, MonadState s m) => MonadState s (WriterT w m) where
  stateI :: WriterT w m ~> StateT s (WriterT w m)
  stateI = writerStateCommute . fmap2 stateI
  stateE :: StateT s (WriterT w m) ~> WriterT w m
  stateE = fmap2 stateE . stateWriterCommute

instance (Monoid w, Functor m, MonadWriter w m) => MonadWriter w (StateT s m) where
  writerI :: StateT s m ~> WriterT w (StateT s m)
  writerI = stateWriterCommute . fmap2 writerI
  writerE :: WriterT w (StateT s m) ~> StateT s m
  writerE = fmap2 writerE . writerStateCommute

-- }}}

-- Writer // RWS {{{

writerRWSCommute :: (Monad m, Monoid o1, Monoid o2) => WriterT o1 (RWST r o2 s m) ~> RWST r o2 s (WriterT o1 m)
writerRWSCommute =
    RWST
  . fmap2 
    ( fmap2 writerStateCommute
    . writerCommute
    )
  . writerReaderCommute
  . fmap2 unRWST

rwsWriterCommute :: (Monad m, Monoid o1, Monoid o2) => RWST r o1 s (WriterT o2 m) ~> WriterT o2 (RWST r o1 s m)
rwsWriterCommute =
    fmap2 RWST
  . readerWriterCommute
  . fmap2 
    ( writerCommute
    . fmap2 stateWriterCommute
    )
  . unRWST

-- }}}

-- State // * --

-- State // RWS {{{

stateRWSCommute :: (Monad m, Monoid o) => StateT s1 (RWST r o s2 m) ~> RWST r o s2 (StateT s1 m)
stateRWSCommute =
    RWST
  . fmap2 
    ( fmap2 stateCommute
    . stateWriterCommute
    )
  . stateReaderCommute
  . fmap2 unRWST

rwsStateCommute :: (Monad m, Monoid o) => RWST r o s1 (StateT s2 m) ~> StateT s2 (RWST r o s1 m)
rwsStateCommute =
    fmap2 RWST
  . readerStateCommute
  . fmap2 
    ( writerStateCommute
    . fmap2 stateCommute
    )
  . unRWST

-- }}}

-- State // List {{{

-- (s -> m [a, s]) -> (s -> m ([a], s))

stateListCommute :: (Functor m, Monoid s) => StateT s (ListT m) ~> ListT (StateT s m)
stateListCommute aMM = ListT $ StateT $ \ s -> ff ^$ unListT $ runStateT s aMM
  where
    ff asL = (concat $ fst ^$ asL, snd ^$ asL)

listStateCommute :: (Functor m) => ListT (StateT s m) ~> StateT s (ListT m)
listStateCommute aMM = StateT $ \ s -> ListT $ ff ^$ runStateT s $ unListT aMM
  where
    ff (s, xs) = (s,) ^$ xs

instance (Functor m, MonadState s m, Functorial Monoid m, Monoid s) => MonadState s (ListT m) where
  stateI :: ListT m ~> StateT s (ListT m)
  stateI = listStateCommute . fmap2 stateI
  stateE :: StateT s (ListT m) ~> ListT m
  stateE = fmap2 stateE . stateListCommute

-- }}}

-- State // ListSet {{{

stateListSetCommute :: (Functor m, JoinLattice s) => StateT s (ListSetT m) ~> ListSetT (StateT s m)
stateListSetCommute aMM = ListSetT $ StateT $ \ s -> ff ^$ unListSetT $ runStateT s aMM
  where
    ff asL = (joins $ fst ^$ asL, snd ^$ asL)

listSetStateCommute :: (Functor m) => ListSetT (StateT s m) ~> StateT s (ListSetT m)
listSetStateCommute aMM = StateT $ \ s -> ListSetT $ ff ^$ runStateT s $ unListSetT aMM
  where
    ff (s, xs) = (s,) ^$ xs

instance (Functor m, MonadState s m, Functorial JoinLattice m, JoinLattice s) => MonadState s (ListSetT m) where
  stateI :: ListSetT m ~> StateT s (ListSetT m)
  stateI = listSetStateCommute . fmap2 stateI
  stateE :: StateT s (ListSetT m) ~> ListSetT m
  stateE = fmap2 stateE . stateListSetCommute

-- }}}

-- State // Kon {{{

stateKonCommute :: StateT s (ContT (s, r) m) ~> ContT r (StateT s m)
stateKonCommute aSK = ContT $ \ (k :: a -> StateT s m r) -> StateT $ \ s ->
  unContT (runStateT s aSK) $ \ (s', a) -> runStateT s' $ k a

konStateCommute :: ContT r (StateT s m) ~> StateT s (ContT (s, r) m)
konStateCommute aKS = StateT $ \ s -> ContT $ \ (k :: (s, a) -> m (s, r)) ->
  runStateT s $ unContT aKS $ \ a -> StateT $ \ s' -> k (s',a)

instance (Monad m, MonadState s m) => MonadState s (ContT r m) where
  stateI :: ContT r m ~> StateT s (ContT r m)
  stateI =
    fmap2 (misoMap2 (stateE, stateI))
    . fmap2 stateKonCommute
    . stateI
    . konStateCommute
    . misoMap2 (stateI, stateE :: StateT s m ~> m)
  stateE :: StateT s (ContT r m) ~> ContT r m
  stateE =
    misoMap2 (stateE, stateI)
    . stateKonCommute
    . stateE
    . fmap2 konStateCommute
    . fmap2 (misoMap2 (stateI, stateE :: StateT s m ~> m))

-- }}}

-- State // OpaqueKon {{{

instance (Monad m, MonadState s m, Isomorphism3 (ContFun r) (k r)) => MonadState s (OpaqueContT k r m) where
  stateI :: OpaqueContT k r m ~> StateT s (OpaqueContT k r m)
  stateI =
    fmap2 opaqueContT
    . stateI
    . metaKonT
  stateE :: StateT s (OpaqueContT k r m) ~> OpaqueContT k r m
  stateE =
    opaqueContT
    . stateE
    . fmap2 metaKonT

-- }}}
