为什么Haskell中的每个类型都不是Eq的一部分? [英] Why isn't every type part of Eq in Haskell?

查看:160
本文介绍了为什么Haskell中的每个类型都不是Eq的一部分?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

或者说,为什么每个数据类型都不能使用(==)?为什么我们必须得出 Eq ourseleves?在其他语言中,比如Python,C ++,当然还有其他语言,它对所有的东西都有一个默认实现!
我想不出无法比较的类型。

解决方案

在Python中,默认的等式实现比较身份,而不是值。这对于用户定义的类非常有用,默认情况下它们是可变的,并且不必具有值的明确定义的概念。但即使在该设置中,使用 is 运算符直接比较可变对象的身份也是比较正常的。



凭借Haskell的不变性和分享这种身份的概念没有多大意义。如果你可以通过身份比较两个术语,你可以找出他们是否被共享,但是一般来说,实现两个可能共享的术语是否是共享的,这样的信息不应该能够影响该程序(除非程序在不同编译器优化策略下改变它们的行为)。

因此,Haskell中的相等性始终是平等;它会告诉您两个词是否代表相同的值(不一定它们是否具有相同的结构;如果您使用无序列表来实现一个集合,那么具有不同结构的两个列表可以代表同一个集合) p>

几乎所有内置类型都已成为 Eq 的成员;最大的例外是函数类型。对于函数来说,唯一真正合理的价值平等概念是扩展平等(它们是否为每个输入返回相同的输出)。我们很想说我们会使用它,让编译器访问函数定义的表示来计算它,但不幸的是,确定两个任意算法(这里用Haskell语法编码)总是产生相同的输出是一个已知的不可计算的问题;如果编译器实际上可以做到这一点,它可以解决暂停问题,我们不必忍受底部值是每种类型的成员。



和不幸的是,函数不能是 Eq 的成员,这意味着很多其他的东西都不可能;可以比较整数列表是否相等,但函数列表不可以,并且每个其他conatiner-ish类型在包含函数时也是如此。这也适用于你编写的ADT,除非有一个明智的平等概念,你可以为这个类型定义不依赖于包含函数的平等(也许这个函数只是一个方便的实现, 函数它不会影响您用ADT表示的值)。

所以,有(1)类型已经是成员 ,(2)不能 3)可以是 Eq 成员的类型,(4)可以的成员的类型Eq ,但只是以一种不明显的方式,以及(5)以明显的方式可以是 Eq 的成员的类型,但程序员会更喜欢另一种方式。我认为Haskell处理这些案例的方式实际上是正确的。 (1)和(2)不需要你的任何东西,(4)和(5)总是要求明确的实例声明。编译器可以帮助你多一点的唯一情况是(3),它可以为你节省12个字符的键入(如果你已经派生任何东西其他)。



我认为这对于成本来说是一个相当小的胜利。编译器必须 来构造一切的一个实例,并假定失败的任何东西都不应该有一个 Eq 实例。目前,如果你想派生一个 Eq 实例并且意外地写出了一个不起作用的类型,那么编译器会告诉你那里和那里有问题。对于提议的隐含地使所有的一切 Eq ,你可以策略,这个错误将显示为一个无法解释的no Eq 实例错误,因此您要转至使用假定的实例。这也意味着,如果我将这种类型想象为表示合理的平等关系不是<简单结构平等的值(上面的情况(5));请记住由无序列表表示的集合? ),并且我忘记编写自己的 Eq 实例,那么编译器可能会自动生成一个错误 Eq 实例给我。我宁愿被告知当你使用它时,你还没有编写 Eq 实例,而不是程序编译并运行编译器!


Or rather, why isn't (==) usable on every data type? Why do we have to derive Eq ourseleves? In other languages, such as Python, C++, and surely others, it has a default implementation for everything! I can't think of types that can't be compared.

解决方案

In Python the default equality implementation compares identity, not value. This is useful for user-defined classes, which by default are mutable and do not have to have a well-defined notion of "value". But even in that setting, it is more normal to use the is operator to directly compare identities for mutable objects.

With Haskell's immutability and sharing this notion of "identity" doesn't make much sense. If you could compare two terms by identity you could find out whether or not they are shared, but it's generally up to the implementation whether two terms that might be shared actually are shared, so such information shouldn't be able to affect the behaviour of the program (unless you like programs that change their behaviour under different compiler optimisation strategies).

So equality in Haskell is always value equality; it tells you whether two terms represent the same value (not necessarily whether they have equal structure; if you implement a set with an unordered list then two lists with different structure can represent the same set).

Almost all of the built in types are members of Eq already; the big exception are function types. The only really sensible notion of value equality for functions is extensional equality (do they return the same output for every input). It's tempting to say we'll use that and let the compiler access a representation of the function definition to compute this, but unfortunately determining whether two arbitrary algorithms (here encoded in Haskell syntax) always produce the same output is a known uncomputable problem; if the compiler could actually do that it could solve the Halting Problem, and we wouldn't have to put up with the bottom value being a member of every type.

And unfortunately the fact that functions can't be members of Eq means lots of other things can't be either; lists of integers can be compared for equality, but lists of functions can't, and the same goes for every other conatiner-ish type when it's containing functions. This also goes for ADTs that you write, unless there is a sensible notion of equality you can define for that type that doesn't depend on the equality of the contained functions (maybe the function is just a convenience in the implementation, and which function it is doesn't affect the value you're representing with ADT).

So, there are (1) types that are already members of Eq, (2) types that can't be members of Eq, (3) types that can be members of Eq in an obvious way, (4) types that can be a member of Eq but only in a non-obvious way, and (5) types that can be members of Eq in an obvious way, but the programmer would prefer an alternative way. I think the way Haskell handles these cases is actually the right way. (1) and (2) don't require anything from you, and (4) and (5) are always going to require an explicit instance declaration. The only case where the compiler could help you out a little more is (3), where it could potentially save you 12 characters of typing (4 if you're already deriving anything else).

I think that would be a pretty small win for the cost. The compiler would have to try to construct an instance of everything and presume that anything for which that fails isn't supposed to have an Eq instance. At the moment if you want to derive an Eq instance and accidentally write a type for which that doesn't work, the compiler tells you then and there that there's a problem. With the proposed "implicitly make everything Eq that you can" strategy, this error would show up as an unexplained "no Eq instance" error at the point that you go to use the assumed instance. It also means that if I'm thinking of the type as representing values for which the reasonable equality relation isn't simple structural equality (case (5) above; remember the set represented by an unordered list?), and I forget to write my own Eq instance, then the compiler might automatically generate a wrong Eq instance for me. I'd much rather be told "you haven't written an Eq instance yet" when I go to use it than have the program compile and run with a bug introduced by the compiler!

这篇关于为什么Haskell中的每个类型都不是Eq的一部分?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆