You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 07:15:05