```{-# LANGUAGE NoImplicitPrelude #-}
module Number.ResidueClass.Reader where

import qualified Number.ResidueClass as Res

import qualified Algebra.PrincipalIdealDomain as PID
import qualified Algebra.IntegralDomain as Integral
import qualified Algebra.Ring           as Ring
import qualified Algebra.Additive       as Additive

import PreludeBase
import NumericPrelude

import Control.Monad (liftM2, liftM4)
-- import Control.Monad.Reader (MonadReader)

import qualified Prelude        as P
import qualified NumericPrelude as NP

{- |
T is a Reader monad but does not need functional dependencies
like that from the Monad Template Library.
-}
newtype T a b = Cons {toFunc :: a -> b}

concrete :: a -> T a b -> b
concrete m (Cons r) = r m

fromRepresentative :: (Integral.C a) => a -> T a a
fromRepresentative = Cons . mod

getZero :: (Additive.C a) => T a a
getZero = Cons \$ const Additive.zero

getOne :: (Ring.C a) => T a a
getOne  = Cons \$ const NP.one

fromInteger :: (Integral.C a) => Integer -> T a a
fromInteger = fromRepresentative . NP.fromInteger

instance Monad (T a) where
(Cons x) >>= y  =  Cons (\r -> toFunc (y (x r)) r)
return = Cons . const

getAdd :: Integral.C a => T a (a -> a -> a)
getAdd = Cons Res.add

getSub :: Integral.C a => T a (a -> a -> a)
getSub = Cons Res.sub

getNeg :: Integral.C a => T a (a -> a)
getNeg = Cons Res.neg

getAdditiveVars :: Integral.C a => T a (a, a -> a -> a, a -> a -> a, a -> a)
getAdditiveVars = liftM4 (,,,) getZero getAdd getSub getNeg

getMul :: Integral.C a => T a (a -> a -> a)
getMul = Cons Res.mul

getRingVars :: Integral.C a => T a (a, a -> a -> a)
getRingVars = liftM2 (,) getOne getMul

getDivide :: PID.C a => T a (a -> a -> a)
getDivide = Cons Res.divide

getRecip :: PID.C a => T a (a -> a)
getRecip = Cons Res.recip

getFieldVars :: PID.C a => T a (a -> a -> a, a -> a)
getFieldVars = liftM2 (,) getDivide getRecip

monadExample :: PID.C a => T a [a]
monadExample =
do (zero',(+~),(-~),negate') <- getAdditiveVars
(one',(*~)) <- getRingVars
((/~),recip') <- getFieldVars
let three = one'+one'+one'  -- is easier if only NP.fromInteger is visible
let seven = three+three+one'
return [zero'*~three, one'/~three, recip' three,
three *~ seven, one' +~ three +~ seven,
zero' -~ three, negate' three +~ seven]

runExample :: [Integer]
runExample =
let three = one+one+one
eleven = three+three+three + one+one
in  concrete eleven monadExample
```