{-# LANGUAGE ConstraintKinds, DataKinds, FlexibleContexts, FlexibleInstances,
             MultiParamTypeClasses, TypeFamilies #-}

{- |
   Module      : Control.Monad.Levels.RWS
   Description : Monads with reader, writer and state abilities
   Copyright   : (c) Ivan Lazar Miljenovic
   License     : 3-Clause BSD-style
   Maintainer  : Ivan.Miljenovic@gmail.com

Note that the original definitions are used for the various reader,
writer and state computations: as such, if there is (for example)
another level that satisfies 'IsReader r' above the one that satisfies
'IsRWS r w s' in the stack, then calling 'ask' will use the higher
level.

 -}
module Control.Monad.Levels.RWS
  ( HasRWS
  , IsRWS
  , module Control.Monad.Levels.Reader
  , module Control.Monad.Levels.Writer
  , module Control.Monad.Levels.State
  ) where

import Control.Monad.Levels
import Control.Monad.Levels.Constraints
import Control.Monad.Levels.Reader
import Control.Monad.Levels.State
import Control.Monad.Levels.Writer

import Data.Monoid (Monoid)

import qualified Control.Monad.Trans.RWS.Lazy   as L
import qualified Control.Monad.Trans.RWS.Strict as S

-- -----------------------------------------------------------------------------

-- | Defined as another class rather than an alias in case you need to
--   ensure the same level satisfies all three constraints (and to
--   have a specific 'ValidConstraint' instance).
class (IsReader r m, IsWriter w m, IsState s m) => IsRWS r w s m

instance (MonadTower m, Monoid w) => IsRWS r w s (L.RWST r w s m)

instance (MonadTower m, Monoid w) => IsRWS r w s (S.RWST r w s m)

instance (Monoid w) => ValidConstraint (IsRWS r w s) where
  type ConstraintSatisfied (IsRWS r w s) m = SameRWS r w s m

type family SameRWS r w s m where
  SameRWS r w s (L.RWST r w s m) = True
  SameRWS r w s (S.RWST r w s m) = True
  SameRWS r w s m                = False

type HasRWS r w s m = SatisfyConstraint (IsRWS r w s) m