WriterT monad变压器

时间:2014-02-08 23:37:13

标签: haskell monads monad-transformers

我正在按照本教程“Monad Transformers Step by Step”

进行操作

http://www.cs.virginia.edu/~wh5a/personal/Transformers.pdf

和eval5中的代码功能来自第2.5节

type Name = String

data Exp = Lit Integer
         | Var Name
         | Plus Exp Exp
         | Abs Name Exp
         | App Exp Exp
         deriving (Show)

data Value = IntVal Integer
           | FunVal Env Name Exp
           deriving (Show)         

type Env = Map.Map Name Value   

type Eval5 a = ReaderT Env (ErrorT String 
                           (WriterT [String] (StateT Integer Identity))) a

runEval5 :: Env -> Integer -> Eval5 a -> ((Either String a, [String]), Integer)                       
runEval5 env st ev = 
    runIdentity (runStateT (runWriterT (runErrorT (runReaderT ev env))) st)


eval5 :: Exp -> Eval4 Value
eval5 (Lit i)           = do tick
                             return $ IntVal i

eval5 (Var n)           = do tick 
                             tell [n]
                             env <- ask
                             case Map.lookup n env of 
                                Nothing  -> throwError("unbound variable: " ++ n)
                                Just val -> return val

eval5 (Plus e1 e2)      = do tick
                             e1' <- eval5 e1
                             e2' <- eval5 e2
                             case (e1', e2') of
                                (IntVal i1, IntVal i2) ->
                                    return $ IntVal $ i1 + i2
                                _ -> throwError "type error in Plus"                                

eval5 (Abs n e)         = do tick
                             env <- ask
                             return $ FunVal env n e

eval5 (App e1 e2)       = do tick
                             val1 <- eval5 e1
                             val2 <- eval5 e2
                             case val1 of
                                FunVal env' n body ->
                                    local (const $ Map.insert n val2 env')
                                          $ eval5 body
                                _ -> throwError "type error in App"

无法使用以下消息进行编译:

No instance for (MonadWriter [Name] Identity)
  arising from a use of `tell'
Possible fix:
  add an instance declaration for (MonadWriter [Name] Identity)
In a stmt of a 'do' block: tell [n]
In the expression:
  do { tick;
       tell [n];
       env <- ask;
       case Map.lookup n env of {
         Nothing -> throwError ("unbound variable: " ++ n)
         Just val -> return val } }
In an equation for `eval5':
    eval5 (Var n)
      = do { tick;
             tell [n];
             env <- ask;
             .... }

ghc version 7.6.3

1 个答案:

答案 0 :(得分:1)

假设tick功能与论文中提到的功能相同:

tick :: Eval5 ()
tick = do n <- get
          put (n+1)

在你的monad变换器中Eval4Eval5之间似乎存在一些混淆。评估员似乎是针对Eval4撰写的。这是固定来源:

import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.Error
import Control.Monad.Identity
import Control.Monad.State

import qualified Data.Map as Map

type Name = String

data Exp = Lit Integer
         | Var Name
         | Plus Exp Exp
         | Abs Name Exp
         | App Exp Exp
         deriving (Show)

data Value = IntVal Integer
           | FunVal Env Name Exp
           deriving (Show)

type Env = Map.Map Name Value

type Eval5 a = ReaderT Env (ErrorT String
                           (WriterT [String] (StateT Integer Identity))) a

runEval5 :: Env -> Integer -> Eval5 a -> ((Either String a, [String]), Integer)
runEval5 env st ev =
    runIdentity (runStateT (runWriterT (runErrorT (runReaderT ev env))) st)

tick :: Eval5 ()
tick = do n <- get
          put (n+1)

eval5 :: Exp -> Eval5 Value
eval5 (Lit i) = do
  tick
  return $ IntVal i

eval5 (Var n) = do
  tick
  tell [n]
  env <- ask
  case Map.lookup n env of
     Nothing  -> throwError("unbound variable: " ++ n)
     Just val -> return val

eval5 (Plus e1 e2) = do
  tick
  e1' <- eval5 e1
  e2' <- eval5 e2
  case (e1', e2') of
     (IntVal i1, IntVal i2) ->
         return $ IntVal $ i1 + i2
     _ -> throwError "type error in Plus"

eval5 (Abs n e) = do
  tick
  env <- ask
  return $ FunVal env n e

eval5 (App e1 e2) = do
  tick
  val1 <- eval5 e1
  val2 <- eval5 e2
  case val1 of
     FunVal env' n body ->
         local (const $ Map.insert n val2 env')
               $ eval5 body
     _ -> throwError "type error in App"

在旁边有一个名为RWS(读/写器/状态)的复合monad变换器,它完全可以完成堆栈的功能。使用它可以简化展开代码。

type EvalRWST a = RWS Env [String] Integer a

runEvalRWS :: Env -> Integer -> EvalRWST a -> (a, Integer, [String])
runEvalRWS env st ev = runRWS ev env st