2014-02-08 53 views
2

我以下這個教程「由步驟單子變壓器步驟」WriterT單子變壓器

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

和從第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" 

在eval5函數的代碼失敗,該消息來編譯:

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版本7.6.3

+2

注意:你可能想[避免使用'WriterT [String]'](http://www.haskellforall.com/2014/02/streaming-logging.html)。 –

+0

我幾天前就讀過這篇博文,感謝:-) –

+0

不客氣! –

回答

1

假設tick功能是在文件中提到的相同的功能:

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

看起來也似乎是在你的單子變壓器周圍Eval4有些混亂VS Eval5。評估人員似乎是針對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(讀/寫/州)的複合單子變壓器,做你的籌碼究竟是幹什麼的。使用它可以相當簡化展開代碼。

type EvalRWST a = RWS Env [String] Integer a 

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

哦,我的。我只是搞亂了eval5型簽名。它應該是eval5 :: Exp - > Eval5的值,而不是eval5 :: Exp - > Eval4的值。 –

+0

我看了RWS變壓器,如果我想給它添加錯誤monad,應該寫下:type Eval6 a = ErrorT String(RWS Env [String] Integer)a。此類型在功能上與Eval5相同,但runEval6的類型是Env - > Integer - > Eval6 a - >(String a,Integer,[String])。我對嗎? –

+0

如果您需要一個錯誤,您可以使用RWST轉換器並將錯誤放在簽名的m類型變量中的堆棧底部。 ''type EvalRWST a = RWST Env [String] Integer Error a''並將runRWS改爲runRWST。 –