环境
对于处理环境,可以使用
如果你选择使用Data.Map模块,值得写
Data.Map.lookup
而不仅仅是lookup,
或者——另一种解决方案——用
隐藏 Prelude 的
lookup
import Prelude hiding (lookup)
这样就不会收到有关两个lookup 函数冲突的错误消息。
为简单起见,我首先使用 Prelude 的更简单的lookup 函数编写解决方案。
为了简单起见,我还没有加入错误处理。
环境:
module Env (Env) where
type Env = [Binding]
type Binding = (Char, Integer)
表达式:
module Expr where
data Expr = Var Char
| Tall Int
| Sum Expr Expr
| Mult Expr Expr
| Neg Expr
| Let Char Expr Expr
评价:
module Semantics (evaluate) where
import Expr (Expr)
import Env (Env)
evaluate :: Expr -> Integer
evaluate = evaluate' []
evaluate' :: Env -> Expr -> Integer
evaluate' _ (Tall n) = n
evaluate' env (Var x) = case lookup x env of
Just n -> n
Nothing -> error ("Variable" ++ [x] ++ "is free!")
evaluate' env (Sum a b) = evaluate' env a + evaluate' env b
evaluate' env (Mult a b) = evaluate' env a * evaluate' env b
evaluate' env (Neg a) = - evaluate' env a
evaluate' env (Let x a b) = evaluate' ((x, a) : env) b
变量冲突
至于规划你的对象语言:在以后的版本中,值得规划一个策略,如何处理冲突的变量名:
let A be 5 in (A +3)
很清楚,但应该是什么意思
let A be 5 in (let A be 3 in A)
?
在您的评估器的早期版本中,您不必对此进行计划,因为lookup 函数将根据其定义中固有的默认行为“自动”决定情况。但是如果你不喜欢它的默认行为,你可能会用一个有意识的策略来增强你的评估器来处理变量名冲突。
错误处理
如果您在环境中计算表达式,并且表达式引用了环境中不包含的变量,那么解释器必须以某种方式报告错误。
你可以用几种方法,最简单的一种:
底部
使用error 函数,您可以强制程序停止并显示用户定义的错误消息。这个方案有缺点,但是写起来容易。
也许
您可以更改您的evaluate' 功能。它不会有签名
evaluate' :: Env -> Expr -> Integer
而是,而是
evaluate' :: Env -> Expr -> Maybe Integer
在这种情况下,你必须严格改变evaluate'的定义。你不能再写了:
evaluate' env (Sum a b) = evaluate' env a + evaluate' env b
但需要更复杂的定义
evaluate' env (Sum a b) = case (evaluate' env a, evaluate' env b) of
(Just a0, Just b0) -> Just (a0 + b0)
_ -> Nothing
我们将 Maybe-ed 整数打包出来,对它们求和,然后将它们打包回 Maybe-ed 整数。这就像在“包内”进行求和。如果我们可以告诉 Haskell 它可以在 Maybe “内部”进行求和,我们可以节省很多工作。
如果我们利用 Maybe 是一个 monad,我们可以做到这一点:我们可以使用为处理 monad 而设计的函数。 Control.Monad 库中提供了此类辅助功能。在这里,liftM2 是一个函数,它可以帮助我们围绕 Maybe-packed 值进行求和:
evaluate' env (Sum a b) = liftM2 (+) (evaluate' env a) (evaluate' env b)
在 Data.Maybe 库中可以找到其他一些用于 Maybe-ed 值的辅助函数,但在这里,我们不需要它们。
module Semantics (evaluate) where
import Expr (Expr)
import Env (Env)
import Control.Monad (liftM, liftM2)
evaluate :: Expr -> Maybe Integer
evaluate = evaluate' []
evaluate' :: Env -> Expr -> Maybe Integer
evaluate' _ (Tall n) = Just n
evaluate' env (Var x) = lookup x env
evaluate' env (Sum a b) = liftM2 (+) (evaluate' env a) (evaluate' env b)
evaluate' env (Mult a b) = liftM2 (*) (evaluate' env a) (evaluate' env b)
evaluate' env (Neg a) = liftM negate (evaluate' env a)
evaluate' env (Let x a b) = evaluate' ((x, a) : env) b