模式与GADT匹配

时间:2013-02-08 18:39:36

标签: haskell

我正在实现一个Expression求解器,但我在模式匹配方面遇到了一些问题。 我有以下代码

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

但是因为eval Add和eval Sub非常相似,我可能想要另一个操作,但我做了更通用的实现,但是我遇到了一些问题。 我虽然喜欢

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 

但它不起作用。可以这样做吗? 提前致谢

3 个答案:

答案 0 :(得分:7)

这不起作用,因为您将Op定义为数据构造函数 和一种类型。类型Op有两个构造函数AddSub,但是 Expression类型具有Op构造函数。这段代码令两者混淆。

eval函数的case语句尝试匹配值 Op,但Op是一个构造函数,在此上下文中接受两个参数, 所以你不能在它上进行模式匹配。我怀疑你会选择类似的东西 此

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

您必须在Op构造函数中包含一个表示内容的字段 要进行操作。因为你必须匹配该操作 无论如何,坚持原来的定义可能会更好 Expression

另一种更简单,更容易扩展的可能性就像是 以下

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)

其中Op用它包装实际函数。你无法做到 做一些好的事情,比如打印出表达式并知道它的功能 虽然对应。

答案 1 :(得分:4)

重审评论:

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)

答案 2 :(得分:0)

对luqui的回答嗤之以鼻:

{-# 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)

现在,您可以在程序中的任何位置添加运算符,而无需更改luqui的Op数据类型。这是一个非常人为的例子:

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类型为Expression Stringeval"31": - )