Haskell,monad Reader表达式的简单评估器

时间:2016-04-09 20:48:39

标签: haskell monads

我确实写了一些表达式的工作评估者。但是,有时我会例外:

*** Exception: Maybe.fromJust: Nothing

我知道它是什么。但是,我无法真正解决它。我的目标是在这种情况下返回Nothing

你可以帮帮我吗?

type Var = String

data Exp = EInt Int
     | EOp  Op Exp Exp
     | EVar Var
     | ELet Var Exp Exp  -- let var = e1 in e2

data Op = OpAdd | OpMul | OpSub

evalExpM :: Exp -> Reader (Map.Map String Int) (Maybe Int)
evalExpM (EInt n) = return $ Just n
evalExpM (EVar var) = ask >>= (\x -> return ( Map.lookup var x))
evalExpM (EOp OpAdd e1 e2) = ask >>= (\x -> return (Just ((fromJust (runReader (evalExpM e1) x)) + (fromJust (runReader (evalExpM e2) x )))))

evalExpM (EOp OpMul e1 e2) = ask >>= (\x -> return (Just ((fromJust (runReader (evalExpM e1) x)) * (fromJust (runReader (evalExpM e2) x )))))

evalExpM (EOp OpSub e1 e2) = ask >>= (\x -> return (Just ((fromJust (runReader (evalExpM e1) x)) - (fromJust (runReader (evalExpM e2) x )))))

evalExpM (ELet var e1 e2) = ask >>= (\x -> (local (Map.insert var (fromJust (runReader (evalExpM e1) x))) (evalExpM e2)) >>= (\y -> return y))

evalExp :: Exp -> Int
evalExp exp = fromJust $ runReader (evalExpM exp) Map.empty

1 个答案:

答案 0 :(得分:3)

首先,使用monadic计算的方法是每次遇到monad中包含的值时都“运行”计算(在本例中为Reader ..)。您应该使用>>=运算符。此外,您正在尝试合并两个monadic效果:ReaderMaybe。执行此操作的“标准”方法是使用monad变换器,但幸运的是Reader(或更确切地说ReaderT)本身就是monad变换器。

此外,您将受益于一些抽象,即:

import Control.Monad.Reader 
import qualified Data.Map as M 

type Env = M.Map String Int 

type EvalM = ReaderT (M.Map String Int) Maybe

lookupEnv :: String -> EvalM Int 
lookupEnv x = ask >>= lift . M.lookup x

withBind :: String -> Int -> EvalM a -> EvalM a 
withBind x v = local (M.insert x v) 

这些函数定义了一个接口,用于处理查找失败的环境。您应该编写这些函数一次,而不是在需要时编写它们的定义。现在你的函数的基本情况是微不足道的:

evalExpM :: Exp -> EvalM Int 
evalExpM (EInt n) = return n 
evalExpM (EVar v) = lookupEnv v 

许多人(包括可能是我自己)会在递归案例中使用applicative运算符,但是你可以避免使用符号汤:

evalExpM (EOp op e1 e2) = liftM2 
  (case op of 
    OpAdd -> (+) 
    OpMul -> (-) 
    OpSub -> (-)
  ) (evalExpM e1) (evalExpM e2) 
evalExpM (ELet var e1 e2) = evalExpM e1 >>= \e -> withBind var e $ evalExpM e2

运行它与以前一样 - 只需将runReader更改为runReaderT - 但现在只有在完成上下文时才运行计算。

evalExp :: Exp -> Int
evalExp e = maybe (error "evalExp") id $ runReaderT (evalExpM e) M.empty 
相关问题