{-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses #-}
module Problem1.CalcMInstance where
import qualified Control.Monad.State as CMS (MonadState, get, put)
import Problem1.Types
newtype StateT s m a = StateT {runStateT :: s -> m (a, s)}
type CalcM = StateT Mem (Either Err)

instance Monad CalcM where return = returnM; (>>=)  = bindM; fail   = failM; 

returnM :: a -> CalcM a
returnM a = StateT (\s-> Right (a, s))

bindM :: CalcM a -> (a -> CalcM b) -> CalcM b
bindM mx f = StateT $ \s-> do -- for the (inner) error monad
  (a, s') <- runStateT mx s 
  runStateT (f a) s'

failM :: String -> CalcM a
failM err = StateT (\s-> fail err)

instance CMS.MonadState Mem (CalcM) where get = getM; put = putM;

getM :: CalcM Mem
getM = StateT $ \s-> return (s, s)

putM :: Mem -> CalcM ()
putM m = StateT $ \s -> return ((), m)