Haskell实现TAPL算术表达式语义时构造子重复声明问题求解
Haskell 默认不允许同一个作用域内的多个代数数据类型使用同名构造子,你遇到的报错正是因为同时在 Expr 和 NV 两个类型里定义了 Zero 和 Succ 导致的。
对应TAPL的语义,有两种常用的实现方案:
方案1:省略单独的NV类型,用谓词区分子集(最推荐)
TAPL 中的nv本身就是Expr的语法子集,不需要单独定义类型,只需要新增一个数值判断函数即可,直接删掉你代码里重复的V和NV类型定义就行,修改后的完整代码如下:
data Expr = Tr | Fl | IfThenElse Expr Expr Expr | IsZero Expr | Succ Expr | Pred Expr | Zero deriving (Show, Eq) -- 判断一个Expr是否为数值,对应书中的nv定义 isNV :: Expr -> Bool isNV Zero = True isNV (Succ t) = isNV t isNV _ = False ssos :: Expr -> Expr -- PAGE 44 -- E-IFTRUE ssos (IfThenElse Tr t2 t3) = t2 -- E-IFFALSE ssos (IfThenElse Fl t2 t3) = t3 -- E-IF ssos (IfThenElse t1 t2 t3) = let t' = ssos t1 in IfThenElse t' t2 t3 -- PAGE 48 -- E-SUCC ssos (Succ t1) = let t' = ssos t1 in Succ t' -- E-PREDZERO ssos (Pred Zero) = Zero -- E-PREDSUCC:E-PRED规则已先对t1求值,走到该分支时Succ的参数必然是nv ssos (Pred (Succ nv1)) = nv1 -- E-PRED ssos (Pred t1) = let t' = ssos t1 in Pred t' -- E-ISZEROZERO ssos (IsZero Zero) = Tr -- E-ISZEROSUCC:同理,走到该分支时Succ的参数必然是nv ssos (IsZero (Succ nv1)) = Fl -- E-ISZERO ssos (IsZero t1) = let t' = ssos t1 in IsZero t'
如果需要严格匹配规则里的nv约束,可以给对应模式加上守卫:
ssos (Pred (Succ nv1)) | isNV nv1 = nv1 ssos (IsZero (Succ nv1)) | isNV nv1 = Fl
方案2:强类型区分NV,用前缀区分构造子
如果你需要在类型层面就禁止非法的数值构造,可以给NV的构造子加前缀避免命名冲突,再定义一个注入构造子把NV转为Expr类型:
data NV = NZero | NSucc NV deriving (Show, Eq) -- 新增NVExpr构造子将数值注入到Expr类型 data Expr = Tr | Fl | IfThenElse Expr Expr Expr | IsZero Expr | Succ Expr | Pred Expr | Zero | NVExpr NV deriving (Show, Eq) -- 后续求值规则对应调整匹配逻辑即可,和书上的语义对应关系不变
内容的提问来源于stack exchange,提问作者dejsdukes
相关产品推荐
相关产品推荐

