我确实写了一些表达式的工作评估者。但是,有时我会例外:
*** 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
答案 0 :(得分:3)
首先,使用monadic计算的方法是每次遇到monad中包含的值时都“运行”计算(在本例中为Reader ..
)。您应该使用>>=
运算符。此外,您正在尝试合并两个monadic效果:Reader
和Maybe
。执行此操作的“标准”方法是使用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