在 Haskell 中,相等测试通常使用来自 Eq
类的 ==
来执行。该函数(在大多数情况下)是在纯 Haskell 术语下定义的,因此它遵循将其递归应用于大数据结构的所有后果。因此,看似微不足道的比较可能会花费大量时间。来吧,这应该是即时的(关于懒惰的提醒):
ghci> let x = [1..100000000] in x == x
True
(2.81 secs, 14,400,130,800 bytes)
为什么 Haskell 在这里不使用引用比较?如果我真的愿意的话,Haskell 是否允许我这样做?
In Haskell, equality test is normally performed using ==
coming the Eq
class. This function is (in most cases) defined under pure Haskell terms, so it follows all consequences of applying it recursively to big data structures. Therefore, a seemingly trivial comparison can take significant amount of time. Come on, this should be instant (reminder about laziness):
ghci> let x = [1..100000000] in x == x
True
(2.81 secs, 14,400,130,800 bytes)
Why doesn't Haskell use comparison by reference here? Does Haskell even allow me to do so if I really want to?
简短的回答:不,这在 Haskell 中是不可能的,并且有很好的理由。引用相等是语言设计的基本部分,保留它使 Haskell 与语言设计领域中的许多其他语言不同。
稍微长一点的答案:这是一个经过充分研究的主题,通常被称为可观察共享,至少可以追溯到 2000 年代初:
Claessen 和 Sands 解释了一些案例,其中的案例 可观察共享很有用,以及如何将其合并到语言中。这是一篇非常容易阅读的论文,它详细解释了这个问题并提出了非保守的扩展。有助于理解基本问题。
Gill 对这个问题的解决方案是一种在实践中使用的非常可行的方法,称为 类型安全的可观察共享。这里的想法是,您可以在纯代码中创建相等性,但只能在单子 IO 上下文中观察它们;它保留了引用平等。它没有假阴性,假阳性也很少。 hackage 上也有这个想法的实现,您可以轻松使用。
长话短说:不,你不能在 Haskell 中进行引用相等、指针相等或直接观察共享,这是有充分理由的。这个问题已经得到了很好的研究和理解,并且在 Haskell 生态系统中有一些实用的解决方案可以在不破坏引用透明度的情况下解决这个问题。
Short answer: No this isn't possible in Haskell, and for very good reasons. Referential equality is a fundamental part of the language design, and preserving it makes Haskell distinct from many other languages in the language design space.
A slightly longer answer: This is a well-studied topic, typically referred to as observable sharing, dating back to at least early 2000s:
Claessen and Sands explain cases in which cases Observable Sharing is useful and how it can be incorporated into the language. This is a very easy to read paper which explains the issue in detail and suggests a non-conservative extension. Good for understanding the basic problems.
Gill's solution to this problem is a very viable approach that is used in practice, called type-safe observable sharing. The idea here is that you can create equalities in pure code, but you can only observe them in the monadic IO context; which preserves referential equality. It has no false negatives, and very few false positives. There is also an implementation of this idea on hackage that you can readily use.
Long story short: No, you cannot do referential-equality, or pointer-equality, or directly observe sharing in Haskell for very good reasons. The problem is well studied and understood, and there are practical solutions to address the issue in the Haskell ecosystem without breaking referential transparency.