如何为使用中缀运算符的数据构造器添加额外参数?
解决带位置参数的中缀数据构造器问题
首先明确报错原因:你的写法Expr :*: Expr Pos会被GHC优先解析为Expr :*: (Expr Pos),但Expr的kind是*(普通类型),不是接受参数的类型构造器* -> *,所以无法像Expr Pos这样使用,导致报错。
中缀数据构造器在Haskell中只能是二元的(即接受两个参数),所以无法直接定义带第三个位置参数的中缀构造器,但有两种方案可以接近你的需求,同时保留中缀的构造和模式匹配风格:
方案一:前缀定义三元构造器,声明为中缀
先给构造器声明中缀优先级,再定义带位置参数的三元构造器:
infixl 7 :*: -- 和原来的优先级保持一致 data Expr = IntL Integer Pos | (:*:) Expr Expr Pos
构造表达式
可以用前缀形式,或者利用中缀语法结合括号:
-- 前缀写法 (:*:) (IntL 10 pos1) (IntL 20 pos2) pos3 -- 中缀写法(GHC会正确解析位置参数为构造器的第三个参数) (IntL 10 pos1) :*: (IntL 20 pos2) pos3
模式匹配
可以用前缀形式,或者更贴近中缀的风格:
eval :: Expr -> Integer eval (IntL n _) = n -- 前缀模式匹配 eval ((:*:) e1 e2 _) = eval e1 * eval e2 -- 中缀风格模式匹配(用反引号包裹构造器) eval (e1 `:*:` e2 _) = eval e1 * eval e2
这种方式完全保留了:*:的中缀语义,只是在构造和匹配时需要处理第三个位置参数,是最接近你需求的方案。
方案二:将位置参数整合为构造器的一部分
如果不想用三元构造器,可以把位置参数作为中缀构造器的左操作数,重新定义构造器:
infixl 7 @*@ data Expr = IntL Integer Pos | Pos @*@ (Expr, Expr)
构造与匹配
-- 构造 pos3 @*@ (IntL 10 pos1, IntL 20 pos2) -- 匹配 eval (pos @*@ (e1, e2)) = eval e1 * eval e2
这种方式语法更直观,但和原有的:*:写法差异稍大。
为什么你的尝试失败?
(Expr :*: Expr) Pos:Haskell不支持这种将中缀表达式作为类型构造器的写法,类型层面的中缀语法仅适用于二元类型构造器。- 前缀形式
(:*:) Expr Expr Pos:这个定义本身是合法的,你觉得无法用相同方式模式匹配,是因为没掌握中缀模式的写法——用反引号包裹构造器即可实现中缀风格的匹配。
内容的提问来源于stack exchange,提问作者Lucas Buhl-Nielsen
相关产品推荐
相关产品推荐

