假设我想定义Mod4模4的整数类型.毕竟,Int是Mod2^64.我能走的一个显而易见的方法是
data Mod4 = ZeroMod4 | OneMod4 | TwoMod4 | ThreeMod4
Run Code Online (Sandbox Code Playgroud)
不过我也可以这样做
data Mod4 = Mod4 Integer
instance Eq Mod4 where
(Mod4 x) == (Mod4 y) = (x-y) `mod` 4 == 0
Run Code Online (Sandbox Code Playgroud)
但是这个功能有问题:
f :: Mod4 -> Mod4
f (Mod4 x) = if x < 20 then Mod4 0 else Mod4 1
Run Code Online (Sandbox Code Playgroud)
f (Mod4 16)不同于f (Mod4 20),而这两个论点是==.所以我最终得到了两种平等:在记忆中的表现(
Mod4 16不同于Mod4 20)和==.
由于所有函数都可以匹配其参数,因此它们始终可以绕过任何==运算符.为什么Haskell没有将内存中的表示作为平等的定义?这样所有类型都变得平凡.
实际上,功能的概念暗示了平等:在给定相等输入时产生相等输出的图形.因此,谈论一个不相等的类型的函数是没有意义的.
chi*_*chi 10
为什么Haskell没有将内存中的表示作为平等的定义?这样所有类型都变得平凡.
不.您无法比较类型的值Integer -> Bool.通常,无法比较函数.
回到黑板上.如何用打字语言设计平等?
一个选项是let (==) :: a -> a -> Bool,如果a是函数则抛出异常.参见例如Ocaml.
另一个选择是在equatable/not equatable中分区类型.这是eqtype在SML中.
另一个但相关的选择是将"eq-ability"表达为对多态性的约束.Eq在哈斯克尔.
现在,Eq可能会更特别.例如,你不能自己定义它的实例,你必须使用deriving Eq,类似于Typeable现在的工作方式.而Haskell设计者则允许用户定义自己的比较函数.用户可能会知道一些"更聪明"的方式.例如,为了比较10场记录,首先比较通常不同的字段,然后比较通常相等的字段,试图提高效率.
请注意,如果我们不导出数据类型构造函数,我们可以使等式成为等价并且仍然有用.例如,Data.Set.Set当它们代表相同的集合时,它们等同于不同的(平衡的)树,但是导出的接口永远不会破坏等价,所以相等看起来像是来自外部的相等.
因此,谈论一个不相等的类型的函数是没有意义的.
的确,当"不等于"在数学意义上被解释时.然而.当它被解释为"等式谓词不可计算"时,它很有意义.我们可以谈论一个函数,它处理类型具有不可判定的相等性的值.