为了证明例如类别法适用于数据类型的某些操作,如何确定如何定义相等?考虑以下类型来表示布尔表达式:
data Exp
= ETrue
| EFalse
| EAnd Exp Exp
deriving (Eq)
尝试证明 Exp 与身份 ETrue 和运营商形成一个类别是否可行:
(<&>) = EAnd
没有重新定义 Eq 实例?使用 Eq 的默认实例 left-identity 法则中断,即:
ETrue <&> e == e
评估为 False 。但是,定义 eval函数:
eval ETrue = True
eval EFalse = False
eval (EAnd e1 e2) = eval e1 && eval e2
和 Eq 实例为:
instance Eq Exp where
e1 == e2 = eval e1 == eval e2
解决了这个问题。是否在(==)方面进行比较是声称满足此类法律的一般要求,还是足以说法律适用于特定类型的平等运算符?
答案 0 :(得分:8)
平等为EVIL。您很少(如果有的话)需要结构平等, 因为太强了。您只需要等效 足够强大 你在做什么。对于范畴理论尤其如此。
在Haskell中,deriving Eq
将为您提供结构上的平等,这意味着您将会这样做
通常希望编写自己的==
/ /=
实现。
一个简单的例子:将有理数定义为整数对,
data Rat = Integer :/ Integer
。如果你使用结构相等(Haskell是什么
deriving
),您将(1:/2) /= (2:/4)
,但作为分数1/2 == 2/4
。什么
你真正关心的是你的元组表示的价值,而不是他们的价值
表示。这意味着您需要等效来比较减少
分数,所以你应该实现它。
旁注:如果使用该代码的某人认为您已定义了结构
等式测试,即用==
检查是否有理由替换数据子组件
通过模式匹配,他们的代码可能会破坏。如果这很重要,
你可以隐藏构造函数以禁止模式匹配,或者可以定义你的
拥有class
(例如,Equiv
与===
和=/=
)来区分这两个概念。 (这个
对于像Agda或Coq这样的定理证明来说,这对Haskell来说非常重要
难以获得实用/现实世界的代码,以致最终出现问题。)
真的很愚蠢(TM)的例子:让我们说那个人想打印一长串的巨大名单
Rat
并认为记住Integer
s的字符串表示将会保存
二进制到十进制转换。有Rat
s的查找表,这样就相等了
Rat
永远不会被转换两次,并且有一个整数查找表。如果
(a:/b) == (c:/d)
,将通过a
- c
/之间的复制来填充缺少的整数条目
b
- d
跳过转换(哎哟!)。对于列表[ 1:/1, 2:/2, 2:/4 ]
,1
获取
转换然后,因为1:/1 == 2:/2
,1
的字符串被复制到。{1}}
2
查询条目。最终结果是"1/1, 1/1, 1/4"
。