content: {-# LANGUAGE TypeFamilies, FlexibleInstances, FlexibleContexts,
               MultiParamTypeClasses, UndecidableInstances, RebindableSyntax,
               DataKinds, TypeOperators, PolyKinds, ConstraintKinds,
               KindSignatures #-}
  
  module Control.Effect.Reader (Reader(..), ask, merge, Mapping(..),
                                Var(..), Submap, Map(..)) where
  
  import Control.Effect
  import Data.Type.Map
  import Prelude hiding (Monad(..))
  import GHC.TypeLits
  import GHC.Exts ( Constraint )
  
  {-| Provides a effect-parameterised version of the class reader monad. Effects
     are sets of variable-type pairs, providing an effect system for reader effects. -}
  
  newtype Reader (s :: [Mapping Symbol *]) a = IxR { runReader :: Map s -> a }
  
  instance Effect Reader where
      type Inv Reader f g = (IsMap f, IsMap g, Split f g (Union f g))
  
      {-| A trivial effect is the empty set -}
      type Unit Reader = '[]
      {-| Effects are combined by set union -}
      type Plus Reader f g = Union f g
  
      {-| Trivially pure computation has an empty reader environment -}
      return x = IxR $ \Empty -> x
      {-| Composing copmutations splits the reader requirements between the two -}
      (IxR e) >>= k = IxR $ \fg -> let (f, g) = split fg
                                   in (runReader $ k (e f)) g
  
  -- Values of the same type can be combined
  type instance Combine v v = v
  
  {-| 'ask' for a variable 'v' of type 'a', raising an effect -}
  ask :: Var v -> Reader '[v :-> a] a
  ask Var = IxR $ \(Ext Var a Empty) -> a
  
  {-| Provides a way to emulated the ImplicitParams features of GHC/Haskell -}
  merge :: (Unionable s t) => (a -> Reader (Union s t) b) -> Reader s (a -> Reader t b)
  merge k = IxR $ \s -> \a -> IxR $ \t -> runReader (k a) (union s t)
  
  {-| If 's' is a subset of 't' then, 's' is a subeffect of 't' -}
  instance Submap s t => Subeffect Reader s t where
      sub (IxR e) = IxR $ \st -> let s = submap st in e s
  
  {-
  {-| Define the operation for removing duplicates using mappend -}
  instance (Nubable (e ': s)) => Nubable (e ': e ': s) where
      nub (Ext _ (Ext x xs)) = nub (Ext x xs)
  -}
  