| Copyright | (c) Andy Gill 2001, (c) Oregon Graduate Institute of Science and Technology, 2001, (C) 2015 KONISHI Yohsuke |
|---|---|
| License | BSD-style (see the file LICENSE) |
| Maintainer | ocean0yohsuke@gmail.com |
| Stability | experimental |
| Portability | --- |
| Safe Haskell | Safe |
| Language | Haskell2010 |
DeepControl.MonadTrans
Description
This module enables you to program in Monad-Transformer style for more deeper level than the usual Control.Monad.Trans module expresses.
You would realize exactly what more deeper level means by reading the example codes, which are attached on the page bottom.
Note: all the MonadTransx instances for Level-4 and Level-5 haven't been written yet.
- class Monad m => MonadIO m where
- class MonadTrans t where
- class MonadTrans2 t where
- class MonadTrans3 t where
- class MonadTrans4 t where
- class MonadTrans5 t where
MonadIO
class Monad m => MonadIO m where
Monads in which IO computations may be embedded.
Any monad built by applying a sequence of monad transformers to the
IO monad will be an instance of this class.
Instances should satisfy the following laws, which state that liftIO
is a transformer of monads:
Instances
MonadTrans
class MonadTrans t where Source
Instances
| MonadTrans ListT Source | |
| MonadTrans MaybeT Source | |
| MonadTrans (ExceptT e) Source | |
| MonadTrans (ReaderT r) Source | |
| MonadTrans (StateT s) Source | |
| Monoid w => MonadTrans (WriterT w) Source | |
| Monoid w => MonadTrans (RWST r w s) Source |
class MonadTrans2 t where Source
Instances
| MonadTrans2 (ReaderT2 r) Source | |
| MonadTrans2 (StateT2 s) Source | |
| Monoid w => MonadTrans2 (WriterT2 w) Source | |
| Monoid w => MonadTrans2 (RWST2 r w s) Source |
class MonadTrans3 t where Source
Instances
| MonadTrans3 (ReaderT3 r) Source | |
| MonadTrans3 (StateT3 s) Source | |
| Monoid w => MonadTrans3 (WriterT3 w) Source | |
| Monoid w => MonadTrans3 (RWST3 r w s) Source |
class MonadTrans4 t where Source
class MonadTrans5 t where Source
Level-1 example
Here is a monad transformer example how to implement a tiny interpreter with RWST-ExceptT-IO monad, a level-1 monad-transformation.
Please turn on three pragmas GeneralizedNewtypeDeriving, FlexibleInstances and MultiParamTypeClasses on this example.
import DeepControl.Applicative
import DeepControl.Monad
import DeepControl.MonadTrans
import DeepControl.Monad.Except
import DeepControl.Monad.RWS
import qualified Data.Map as M
-- ----------------------------------------------
-- Data-types
type Name = String -- variable names
-- Expression
data Exp = Lit Int -- Literal
| Var Name -- Variable
| Plus Exp Exp -- (+)
| Lam Name Exp -- λ
| App Exp Exp -- Application
deriving (Show)
-- Value
data Value = IntVal Int -- Int Value
| FunVal Env Name Exp -- Functional Value
deriving (Show)
-- Environment
type Env = M.Map Name Value -- mapping from names to values
-- ----------------------------------------------
-- Monad-Transform
type EvalError = String
instance Error EvalError where
strMsg x = x
newtype Eval a = Eval (RWST Env [String] Int
(ExceptT EvalError
IO) a)
deriving (Functor, Applicative, Monad, MonadIO)
unEval :: Eval a -> (RWST Env [String] Int
(ExceptT EvalError
IO) a)
unEval (Eval a) = a
runEval :: Eval a
-> Env -- Reader
-> Int -- States
-> IO (Either EvalError (a, Int, [String]))
runEval (Eval x) env state = x >- \x -> runRWST x env state
>- runExceptT
instance MonadError EvalError Eval where
throwError = Eval . trans . throwError
catchError x h =
let x' = unEval x
in Eval $ (liftCatch catchError x') (\e -> unEval (h e))
instance MonadReader Env Eval where
ask = Eval $ ask
local x = Eval . (local x) . unEval
instance MonadWriter [String] Eval where
writer = Eval . writer
listen m = Eval $ (listen (unEval m))
pass m = Eval $ (pass (unEval m))
instance MonadState Int Eval where
get = Eval $ get
put = Eval . put
state = Eval . state
-- ----------------------------------------------
-- Interpreter
tick :: (Num s, MonadState s m) => m ()
tick = do
st <- get
put (st + 1)
eval :: Exp -> Eval Value
eval (Lit i) = do
tick
liftIO $ print i
return $ IntVal i
eval (Var n) = do
tick
tell [n]
env <- ask
case M.lookup n env of
Nothing -> throwError $ "unbound variable: " ++ n
Just val -> return val
eval (Plus e1 e2) = do
tick
e1' <- eval e1
e2' <- eval e2
case (e1', e2') of
(IntVal i1, IntVal i2) -> return $ IntVal (i1 + i2)
_ -> throwError "type error in addition"
eval (Lam n e) = do
tick
env <- ask
return $ FunVal env n e
eval (App e1 e2) = do
tick
val1 <- eval e1
val2 <- eval e2
case val1 of
FunVal env' n body -> local (const (M.insert n val2 env')) $ eval body
_ -> throwError "type error in application"
-- ----------------------------------------------
-- Examples
-- 12 + ((\x -> x) (4 + 2))
exp1 :: Exp
exp1 = Lit 12 `Plus` ((Lam "x" (Var "x")) `App` (Lit 4 `Plus` Lit 2))
-- (\x -> (\y -> x + y)) a b
exp2 :: Exp
exp2 = (Lam "x" (Lam "y" ((Var "x") `Plus` (Var "y")))) `App` (Var "a") `App` (Var "b")
-- An environment
env :: Env
env = M.fromList [ ("a", IntVal 1)
, ("b", IntVal 2)
, ("c", IntVal 3)
, ("d", IntVal 4)
]
-- ----------------------------------------------
-- Tests
--
-- > runEval (eval exp1) env 0
-- 12
-- 4
-- 2
-- Right (IntVal 18,8,["x"])
-- > runEval (eval exp2) env 0
-- Right (IntVal 3,9,["a","b","x","y"])
-- > runEval (eval $ Var "x") env 0
-- Left "unbound variable: x"Level-2 example
Here is a monad transformer example how to implement Polish Notation with StateT2-IO-Maybe monad, a level-2 monad-transformation.
import DeepControl.Applicative
import DeepControl.Commutative (cmap)
import DeepControl.Monad
import DeepControl.Monad.State
import DeepControl.MonadTrans
-----------------------------------------------
-- State
push :: a -> State [a] a
push x = do
xs <- get
put (x:xs)
return x
pop :: State [a] a
pop = do
xs <- get
put (tail xs)
return (head xs)
-- > runState (push 1 >> push 2 >> push 3) []
-- (3,[3,2,1])
-- > runState (push 1 >> push 2 >> push 3 >> pop >> pop) []
-- (2,[1])
poland :: String -> State [Double] Double
poland "+" = do
x <- pop
y <- pop
push (y + x)
poland "-" = do
x <- pop
y <- pop
push (y - x)
poland "*" = do
x <- pop
y <- pop
push (y * x)
poland "/" = do
x <- pop
y <- pop
push (y / x)
poland x = push (read x :: Double)
poland_calc :: [String] -> Double
poland_calc xs = evalState (cmap poland xs >> pop) []
-- > poland_calc ["1","2","*"]
-- 2.0
-- > poland_calc ["1","2","-"]
-- -1.0
-- > poland_calc ["1","2","+","3","*"]
-- 9.0
-- > poland_calc ["1","2","+","3","*","3","/"]
-- 3.0
-- > poland_calc ["1","2","+","3","*","0","/"]
-- Infinity
-----------------------------------------------
-- StateT-Maybe
pushT :: a -> StateT [a] Maybe a
pushT x = do
xs <- get
put (x:xs)
return x
popT :: StateT [a] Maybe a
popT = do
xs <- get
put (tail xs)
return (head xs)
-- > runStateT (pushT 1 >> pushT 2 >> pushT 3) []
-- Just (3,[3,2,1])
-- > runStateT (pushT 1 >> pushT 2 >> pushT 3 >> popT >> popT) []
-- Just (2,[1])
polandT :: String -> StateT [Double] Maybe Double
polandT "+" = do
x <- popT
y <- popT
pushT (y + x)
polandT "-" = do
x <- popT
y <- popT
pushT (y - x)
polandT "*" = do
x <- popT
y <- popT
pushT (y * x)
polandT "/" = do
x <- popT
y <- popT
trans $ guard (x /= 0)
pushT (y / x)
polandT x = pushT (read x :: Double)
poland_calcT :: [String] -> Maybe Double
poland_calcT xs = evalStateT (cmap polandT xs >> popT) []
-- > poland_calcT ["1","2","*"]
-- Just 2.0
-- > poland_calcT ["1","2","-"]
-- Just (-1.0)
-- > poland_calcT ["1","2","+","3","*"]
-- Just 9.0
-- > poland_calcT ["1","2","+","3","*","3","/"]
-- Just 3.0
-- > poland_calcT ["1","2","+","3","*","0","/"]
-- Nothing
-----------------------------------------------
-- StateT2-IO-Maybe
pushT2 :: a -> StateT2 [a] IO Maybe a
pushT2 x = do
xs <- get
put (x:xs)
return x
popT2 :: StateT2 [a] IO Maybe a
popT2 = do
xs <- get
put (tail xs)
return (head xs)
polandT2 :: String -> StateT2 [Double] IO Maybe Double
polandT2 "+" = do
x <- popT2
y <- popT2
liftIO $ putStrLn (show y ++" + "++ show x ++" = "++ show (y + x))
pushT2 (y + x)
polandT2 "-" = do
x <- popT2
y <- popT2
liftIO $ putStrLn (show y ++" - "++ show x ++" = "++ show (y - x))
pushT2 (y - x)
polandT2 "*" = do
x <- popT2
y <- popT2
liftIO $ putStrLn (show y ++" * "++ show x ++" = "++ show (y * x))
pushT2 (y * x)
polandT2 "/" = do
x <- popT2
y <- popT2
liftIO $ putStr (show y ++" / "++ show x ++" = ")
trans2.(*:) $ guard (x /= 0)
liftIO $ putStr (show (y / x) ++"\n")
pushT2 (y / x)
polandT2 x = pushT2 (read x :: Double)
poland_calcT2 :: [String] -> IO (Maybe Double)
poland_calcT2 xs = evalStateT2 (cmap polandT2 xs >> popT2) []
-- > poland_calcT2 ["1","2","*"]
-- 1.0 * 2.0 = 2.0
-- Just 2.0
-- > poland_calcT2 ["1","2","+","3","*"]
-- 1.0 + 2.0 = 3.0
-- 3.0 * 3.0 = 9.0
-- Just 9.0
-- > poland_calcT2 ["1","2","+","3","*","3","/"]
-- 1.0 + 2.0 = 3.0
-- 3.0 * 3.0 = 9.0
-- 9.0 / 3.0 = 3.0
-- Just 3.0
-- > poland_calcT2 ["1","2","+","3","*","0","/"]
-- 1.0 + 2.0 = 3.0
-- 3.0 * 3.0 = 9.0
-- 9.0 / 0.0 = Nothing