【问题标题】:Haskell: Matching two expressions that are not from class EqHaskell:匹配两个不属于 Eq 类的表达式
【发布时间】:2014-10-20 17:53:12
【问题描述】:

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

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

case exp1 of 
     exp2 -> ...

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

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

编辑

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

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

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

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

提前致谢

【问题讨论】:

  • 是什么阻止您定义Eq 实例?
  • 我没有定义这些类型。它们在库中定义。独立它们有很多构造函数和子类型等。
  • 有点不清楚你在做什么。如果可以使用模式匹配,则不一定需要 Eq 实例。你能举一个更具体的例子吗?
  • @bheklilr 我希望现在有了这个解释,您可以更好地理解我的问题。
  • @SalvadorTamarit 如果它是开源的,您能否提供指向该库的链接?如果只是有大量的构造函数,但没有什么能阻止你检查语法相等(例如 HOAS),那么真的应该有一个 deriving Eq

标签: haskell pattern-matching expression comparable


【解决方案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

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


如果您没有Data 实例或者,但数据类型足够简单,比较相等性是 100% 直接的,那么StandaloneDeriving 是要走的路,就像@ ChristianConkle 表示。为此,您需要在文件顶部添加 {-# LANGUAGE StandaloneDeriving #-} pragma 并添加多个子句

deriving instance Eq a => Eq (CStatement a)

对于没有Eq 实例的CStatement 使用的每种类型都有一个,例如CAttribute。 GHC 会抱怨你需要的每一个,所以你不必去寻找源头。

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

【讨论】:

  • 我认为这是一个比我更好的答案,尤其是因为它实际上回答了这个问题。我认为StandaloneDeriving 解决方案在这种情况下实际上更好(不那么黑客),但假设它有效。我会把它放在答案的第一位。正如您所说明的那样,Data 让您很快就进入了统一的 Haskell 杂草。 a -> b -> Bool?!哎哟。
  • 关于orphan instances 的警告说明可能是合适的,尤其是因为提问者似乎是 Haskell 的新手。 (并且对 Haskell 的怪癖很感兴趣……)
  • @ChristianConkle 最简洁的解决方案确实是StandaloneDeriving 加上选择性导入实例,但我们在语言/GHC 中没有(还没有?)。我已经在最后提到了一些关于孤儿实例的内容,但我非常愿意接受更强大的措辞和/或大量使用红色的编辑!
  • 感谢@yatima2975,解决了我的问题。
【解决方案2】:

我不熟悉 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 实例。但是还有许多其他库公开其类型的实现,而是要求您使用提供的函数(和类实例)。 Haskell 允许这种抽象。库作者可以依赖这样一个事实,即消费者不能乱搞实现细节(至少不使用像 unsafeCoerce 这样的不安全工具)。

【讨论】:

  • 库如下:hackage.haskell.org/package/language-c-0.4.6 最有趣的模块是这个:hackage.haskell.org/package/language-c-0.4.6/docs/… 我需要比较CStatement的元素
  • 天哪。乍一看,实例Eq a =&gt; Eq (CStatement a) 可以被编写或派生。你可以尝试StandaloneDeriving;您可能需要CStatementCExpression 和许多其他类型的孤儿实例; GHC 会告诉你。您也可以考虑与库的维护者讨论这个问题。
  • 在这种情况下,正如@yatima2975 所建议的那样,它似乎存在一个更简单的解决方案。在库 Data.Generics.Twins 中使用 geq。你的方法也行得通。但是让我很意外的是,在 Haskell 中没有办法定义一种“动态模式”,每次都得依赖于派生类 Eq。
  • 感谢@ChristianConkle 澄清问题并提供有趣的替代方案。但是,我仍然不明白为什么在 private interface 的情况下,这种匹配没有意义(根据 GHC)。一个人可能对了解数据结构的实现不感兴趣,但可能对比较两个不同的实例感兴趣,这与结果是否有意义无关。当然,通过派生Eq,它提供了预期的结果。我的意思是,如果最终有可能做的事情,为什么不直接允许它们呢?而且我认为这不是那么 :)
  • 我仍然不完全相信,但this post 帮助我更好地理解了它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多