首先,我想澄清一点,我试图找到解决我的问题的谷歌搜索,但我没有成功。
我需要一种方法来比较两个表达式。问题是这些表达方式无法比较。我来自Erlang,我可以在那里:
case exp1 of
exp2 -> ...
绑定exp1
和exp2
的位置。但是哈斯克尔不允许我这样做。但是,在Haskell中,我可以使用==
进行比较。不幸的是,他们的类型不是类Eq
的成员。当然,这两个表达式在运行时都是未知的,因此我无法在源代码中编写静态模式。
如何在不必定义自己的比较函数的情况下比较这两个表达式?我认为模式匹配可以在某种程度上使用(如在Erlang中),但我不知道如何。
我认为解释我的目标有助于理解这个问题。
我正在编写一个抽象语法树(AST)。我将应用一组将要修改此AST的规则,但我想将修改存储在列表中。此列表的元素应该是AST的原始部分及其修改的元组。所以最后一步是为每个元组搜索完全相同的AST片段,并用元组的第二个元素替换它。所以,我需要这样的东西:
change (old,new):t piece_of_ast =
case piece_of_ast of
old -> new
_ -> piece_of_ast
我希望这个解释澄清我的问题。
提前致谢
答案 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 == undefined
或undefined == 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不能自动做“这种事”。有几个原因。在某种程度上它是历史的;部分原因是deriving
,Eq
满足了大部分需求。
但是还有一个重要原则:类型的公共接口理想地表示您可以使用该类型的值实际执行的操作。你的特定类型是一个不好的例子,因为它暴露了它的所有构造函数,而且它看起来真的应该应该成为它的Eq
实例。但是还有许多其他库 not 公开其类型的实现,而是要求您使用提供的函数(和类实例)。 Haskell允许抽象;图书馆作者可以依赖这样一个事实,即消费者无法了解实施细节(至少不使用unsafeCoerce
等不安全的工具)。