{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts,
UndecidableInstances, GeneralizedNewtypeDeriving #-}
module Problem3.QuestionCode where
import Problem2.Prod -- contains the |Prod m n| monad instance from Problem 2
import qualified Control.Monad.Identity as CMI
import qualified Control.Monad.State as CMS
import qualified Control.Monad.Error as CME
instance (CMS.MonadState s m, CMS.MonadState s n) => CMS.MonadState s (Prod m n) where
get = Prod (CMS.get, CMS.get)
put s = Prod (CMS.put s, CMS.put s)
instance (CME.MonadError e m, CME.MonadError e n) => CME.MonadError e (Prod m n) where
throwError e = Prod (CME.throwError e, CME.throwError e)
catchError mnx f = Prod ( CME.catchError (fstP mnx) (fstP . f)
, CME.catchError (sndP mnx) (sndP . f) )
type Store = Integer
type Err = String
newtype Eval1 a = Eval1{ unEval1 :: CMS.StateT Store (CME.ErrorT Err CMI.Identity) a }
deriving (Monad, CMS.MonadState Store, CME.MonadError Err)
newtype Eval2 a = Eval2{ unEval2 :: CME.ErrorT Err (CMS.StateT Store CMI.Identity) a }
deriving (Monad, CMS.MonadState Store, CME.MonadError Err)
startStateFrom :: Monad m => state -> CMS.StateT state m a -> m a
startStateFrom = flip CMS.evalStateT
emptyStore :: Store
emptyStore = 0
runEval1 :: Eval1 a -> Either Err a
runEval1 = CMI.runIdentity . CME.runErrorT . startStateFrom emptyStore . unEval1
runEval2 :: Eval2 a -> Either Err a
runEval2 = CMI.runIdentity . startStateFrom emptyStore . CME.runErrorT . unEval2
(-*-) :: (a1->a2) -> (b1->b2) -> (a1,b1) -> (a2,b2)
f -*- g = \(a,b)->(f a, g b)
type Test = Prod Eval1 Eval2
check :: Test a -> (Either Err a, Either Err a)
check = (runEval1 -*- runEval2) . unProd
test1 :: (CME.MonadError Err m, CMS.MonadState Store m) => m Store
test1 = (do CMS.put 1738; CME.throwError "hello"; CMS.get)
`CME.catchError` \e-> CMS.get
main = print (check test1)