Haskell:匹配两个不是来自Eq类的表达式

时间:2014-10-20 17:53:13

标签: haskell pattern-matching expression comparable

首先,我想澄清一点,我试图找到解决我的问题的谷歌搜索,但我没有成功。

我需要一种方法来比较两个表达式。问题是这些表达方式无法比较。我来自Erlang,我可以在那里:

case exp1 of 
     exp2 -> ...

绑定exp1exp2的位置。但是哈斯克尔不允许我这样做。但是,在Haskell中,我可以使用==进行比较。不幸的是,他们的类型不是类Eq的成员。当然,这两个表达式在运行时都是未知的,因此我无法在源代码中编写静态模式。

如何在不必定义自己的比较函数的情况下比较这两个表达式?我认为模式匹配可以在某种程度上使用(如在Erlang中),但我不知道如何。

修改

我认为解释我的目标有助于理解这个问题。

我正在编写一个抽象语法树(AST)。我将应用一组将要修改此AST的规则,但我想将修改存储在列表中。此列表的元素应该是AST的原始部分及其修改的元组。所以最后一步是为每个元组搜索完全相同的AST片段,并用元组的第二个元素替换它。所以,我需要这样的东西:

 change (old,new):t piece_of_ast =
     case piece_of_ast of 
          old -> new
          _ -> piece_of_ast

我希望这个解释澄清我的问题。

提前致谢

2 个答案:

答案 0 :(得分:1)

这可能是图书馆的一个疏忽(但也许我错过了Eq是个坏主意的一个微妙原因!)我会联系维护者以获得所需的Eq实例

但是对于记录而且同时,如果您要比较的类型没有Eq的实例,但是Data有一个实例,那么您可以执行以下操作你问题就是这样。

Data.Generics.Twins包提供了相等的通用版本,类型为

geq :: Data a => a -> a -> Bool

正如文档所述,这是'通用平等:“导出Eq”的替代方法。它通过比较顶层构造函数来工作,如果它们是相同的继续到子项。

由于Data类继承自Typeable,您甚至可以编写类似

的函数
veryGenericEq :: (Data a, Data b) => a -> b -> Bool
veryGenericEq a b = case (cast a) of
    Nothing -> False
    Maybe a' -> geq a' b

但我不确定这是一个好主意 - 它肯定是unhaskelly,将所有类型粉碎成一个快乐的大宇宙: - )


如果您没有<{1}}实例,但数据类型足够简单,比较相等是100%直截了当,那么Data是通往去吧,正如@ChristianConkle所说。为此,您需要在文件顶部添加StandaloneDeriving编译指示并添加一些子句

{-# LANGUAGE StandaloneDeriving #-}

每种类型deriving instance Eq a => Eq (CStatement a) 使用一个没有CStatement实例的用户,例如Eq。 GHC会抱怨你需要的每一个,所以你不必拖网搜索。

这将创建一堆所谓的“孤儿实例”。通常,类似CAttribute的实例将在定义instance C T where的模块或定义C的模块中定义。你的实例是'孤儿',因为他们与'父母'分开了。孤立实例可能不好,因为您可能会开始使用新的库,已定义了这些实例;编译器应该使用哪个实例?有一点note on the Haskell wiki about this issue。如果您没有发布图书馆供其他人使用,那就没问题;这是你的问题,你可以处理它。它也适用于测试;如果你可以实现T,那么图书馆维护者可能会在库中包含Eq,解决你的问题。

答案 1 :(得分:0)

我不熟悉Erlang,但Haskell并不认为可以比较所有表达式的相等性。例如,请考虑undefined == undefinedundefined == let x = x in x

使用(==)进行的平等测试是对的操作。某些类型的值根本无法比较。例如,考虑两个IO String类型的值:return "s"getLine。他们是平等的吗?如果你运行程序并输入“s”怎么办?

另一方面,考虑一下:

f :: IO Bool
f = do
      x <- return "s" -- note that using return like this is pointless
      y <- getLine
      return (x == y) -- both x and y have type String.

目前尚不清楚您在寻找什么。模式匹配可能是答案,但如果您使用的库类型不是Eq的实例,那么可能的答案是比较两个值实际上是不可能的(或者库作者由于某种原因决定了施加限制)。

具体来说,您要比较哪些类型?

编辑:作为提及的评论者,您还可以使用Data进行比较。在这种情况下,我不知道这对你来说是否更容易,但对我而言,这是不同寻常的;黑客攻击。

你也问过为什么Haskell不能自动做“这种事”。有几个原因。在某种程度上它是历史的;部分原因是derivingEq满足了大部分需求。

但是还有一个重要原则:类型的公共接口理想地表示您可以使用该类型的值实际执行的操作。你的特定类型是一个不好的例子,因为它暴露了它的所有构造函数,而且它看起来真的应该应该成为它的Eq实例。但是还有许多其他库 not 公开其类型的实现,而是要求您使用提供的函数(和类实例)。 Haskell允许抽象;图书馆作者可以依赖这样一个事实,即消费者无法了解实施细节(至少不使用unsafeCoerce等不安全的工具)。