Haskell中含函数类型的Val无法派生Show,如何打印表达式求值结果
报错原因
你的Val类型包含Vprim (Val->Val)构造器,其中的函数字段没有默认的Show实例,而Haskell自动派生Show要求类型的所有字段都必须实现Show类,因此无法自动deriving (Show)。
解决方法:手动实现Show实例
你不需要依赖自动派生,手动为Val类型实现Show类即可,对于函数类型的字段不需要打印具体实现,直接输出占位提示即可:
instance Show Val where show (Vnum n) = show n show (Vprim _) = "<primitive function>"
另外你当前的eval函数仅实现了常量的求值逻辑,其他表达式分支未实现,补全后的完整可运行代码如下:
type Var = String -- 抽象语法树表示的源代码表达式 data Exp = Enum Int -- 常量 | Evar Var -- 变量 | Elet Var Exp Exp -- "let x = e1 in e2" 表达式 | Ecall Exp Exp -- 函数调用 deriving (Show) -- 求值返回值 data Val = Vnum Int -- 整数 | Vprim (Val->Val) -- 原语函数 -- 手动实现Show实例 instance Show Val where show (Vnum n) = show n show (Vprim _) = "<primitive function>" mkPrim :: (Int->Int->Int)->Val mkPrim f = Vprim(\(Vnum x) -> Vprim (\(Vnum y) -> Vnum (f x y))) -- 包含所有原语函数的初始环境 type Env = [(Var, Val)] pervasive :: Env pervasive = [("+", mkPrim (+)), ("-", mkPrim (-)),("*", mkPrim (*)), ("/", mkPrim div)] -- 补全所有分支的求值函数 eval :: Env->Exp->Val eval env (Enum n) = Vnum n eval env (Evar x) = case lookup x env of Just v -> v Nothing -> error $ "未找到变量: " ++ x eval env (Elet x e1 e2) = let v1 = eval env e1 in eval ((x, v1):env) e2 eval env (Ecall e1 e2) = let Vprim f = eval env e1 in f (eval env e2) sampleExp = Elet "x" (Enum 3) (Ecall (Ecall (Evar "+") (Evar "x")) (Enum 4)) main = do print(eval pervasive (Enum 4)) print(eval pervasive sampleExp) -- 测试样例表达式,输出结果为7
运行后输出:
4 7
扩展优化(可选)
如果需要区分不同的原语函数(比如打印+、-等符号),可以修改Val的定义,给Vprim加一个名字字段:
data Val = Vnum Int | Vprim String (Val -> Val) instance Show Val where show (Vnum n) = show n show (Vprim name _) = "<" ++ name ++ ">"
对应的mkPrim和初始环境同步修改即可:
mkPrim :: String -> (Int->Int->Int)->Val mkPrim name f = Vprim name (\(Vnum x) -> Vprim name (\(Vnum y) -> Vnum (f x y))) pervasive :: Env pervasive = [("+", mkPrim "+" (+)), ("-", mkPrim "-" (-)),("*", mkPrim "*" (*)), ("/", mkPrim "/" div)]
这样打印原语函数的时候就会显示<+>、<->等对应名字,更方便调试。
内容的提问来源于stack exchange,提问作者Rafael Gonzalez
相关产品推荐
相关产品推荐

