[英]Pattern match with GADTs
I am implementing an Expression solver, but I am having some problems with pattern matching. 我正在实现一个表达式求解器,但是在模式匹配方面遇到了一些问题。 I have the following code
我有以下代码
data Expression a where
Const ∷ Int → Expression Int
Add ∷ Expression Int → Expression Int → Expression Int
Sub ∷ Expression Int → Expression Int → Expression Int
eval ∷ Expression a → a
eval (Const a) = a
eval (Add exp1 exp2) = (val1 + val2)
where
val1 = eval exp1
val2 = eval exp2
eval (Sub exp1 exp2) = (val1 - val2)
where
val1 = eval exp1
val2 = eval exp2
But since eval Add and eval Sub are very similar and I could want another operations I though of doing a more generic implementation, but I am having some problems. 但是由于eval Add和eval Sub非常相似,尽管我想进行更通用的实现,但我可能想要另一个操作,但是我遇到了一些问题。 I though of doing like
我虽然喜欢
data Op = Add | Sub
data Expression a where
Const ∷ Int → Expression Int
Op ∷ Expression Int → Expression Int → Expression Int
eval (Op exp1 exp2) = case Op of
Add → (val1 + val2)
Sub → (val1 - val2)
where
val1 = eval exp1
val2 = eval exp2
But it doesn't work. 但这是行不通的。 Is it possible to do something like this?
有可能做这样的事情吗? Thanks in advance
提前致谢
This does not work because you are defining Op
as both a data constructor and a type. 这不起作用,因为您将
Op
定义为数据构造函数和类型。 The type Op
has two constructors Add
and Sub
, but the Expression
type has an Op
constructor. Op
类型具有两个构造函数Add
和Sub
,而Expression
类型具有Op
构造函数。 This code is confusing the two. 此代码使两者混淆。
The case statement of your eval
function attempts to match of the value Op
, but Op
is a constructor that takes two arguments in this context, so you can't pattern match on it. 您的
eval
函数的case语句尝试匹配值Op
,但是Op
是在此上下文中带有两个参数的构造函数,因此您无法对其进行模式匹配。 I suspect you are going for something like this 我怀疑你要这样
data Op = Add | Sub
data Expression a where
Const :: Int -> Expression Int
Op :: Op -> Expression Int -> Expression Int -> Expression Int
eval (Const c) = c
eval (Op op exp1 exp2) = case op of
Add -> (val1 + val2)
Sub -> (val1 - val2)
where
val1 = eval exp1
val2 = eval exp2
You will have to include a field in the Op
constructor that denotes what operation is to be performed. 您将必须在
Op
构造函数中包含一个字段,该字段指示要执行的操作。 Since you have to match on the that operation anyway, it would probably be nicer to stick with the original definition of Expression
. 由于无论如何您都必须在that操作上进行匹配,因此最好坚持使用
Expression
的原始定义。
Another possibility that is simpler and easier to extend might be something like the following 另一个更容易扩展的可能性可能是如下所示
data Expression a where
Const :: Int -> Expression Int
Op :: (a -> b -> c) -> Expression a -> Expression b -> Expression c
eval :: Expression a -> a
eval (Const c) = c
eval (Op f exp1 exp2) = f (eval exp1) (eval exp2)
where an Op
wraps the actual function up with it. Op
在其中包装实际功能。 You would not be able to do nice things like print out the expression and know what function it corresponds to though. 您将无法完成诸如打印出表达式并知道其对应的功能之类的出色工作。
Riffing on the comments: 对评论发表评论:
data Op a b c where
Add :: Op Int Int Int
Sub :: Op Int Int Int
Less :: Op Int Int Bool
interpretOp :: Op a b c -> a -> b -> c
interpretOp Add = (+)
interpretOp Sub = (-)
interpretOp Less = (<)
data Expression a where
Const :: Int -> Expression Int
Op :: Op a b c -> Expression a -> Expression b -> Expression c
eval :: Expression a -> a
eval (Const x) = x
eval (Op op a b) = interpretOp op (eval a) (eval b)
Riffing on luqui's answer: 轻视卢基的答案:
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, GADTs #-}
class OpLike op a b where
type Result op a b
interpret :: op -> a -> b -> Result op a b
data Expression a where
Const :: a -> Expression a
Op :: OpLike op a b => op -> Expression a -> Expression b -> Expression (Result op a b)
eval :: Expression a -> a
eval (Const x) = x
eval (Op op a b) = interpret op (eval a) (eval b)
Now you can add operators at any point in your program, without having to alter something like luqui's Op
datatype. 现在,您可以在程序的任何位置添加运算符,而无需更改诸如luqui的
Op
数据类型之类的内容。 Here's a very contrived example: 这是一个非常人为的示例:
data Add = Add
add x y = Op Add x y
instance OpLike Add Int Int where
type Result Add Int Int = Int
interpret Add x y = x + y
instance OpLike Add Int Bool where
type Result Add Int Bool = String
interpret Add x y = if y then reverse (show x) else show x
example = (Const (3::Int) `add` Const (10::Int)) `add` (Const True)
example
has type Expression String
and eval
uates to "31"
:-) example
类型为Expression String
, eval
为"31"
:-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.