模式与GADT匹配

Jor*_*gel 1 haskell

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

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
Run Code Online (Sandbox Code Playgroud)

但是因为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 
Run Code Online (Sandbox Code Playgroud)

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

sab*_*uma 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
Run Code Online (Sandbox Code Playgroud)

您必须在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)
Run Code Online (Sandbox Code Playgroud)

其中一个Op与它包装实际功能了.你不可能做出很好的事情,比如打印出表达式并知道它对应的功能.

  • 你可以做的另一件事:`数据操作在哪里添加::操作Int`,`解释::操作 - > - > - > a - > a; 解释Add =(+)`和`eval Op op exp1 exp2 =解释op(eval exp1)(eval exp2)`.请注意,这不一定是更好或更糟. (2认同)