Haskell有时被称为“以等价取代等价”。下面的代码显示在这种句子的每种解释下都不是正确的。 Wikipedia遵循这一原则,即说f(x)=f(x)
每一项,x
但似乎没有任何人可以测试的实际逻辑内容,这是通过反身法则,重言式实现的。
我觉得做一个合乎逻辑的要求像这更像是莱布尼茨的法律(或没有区别identicals),其中所需要的措辞
x=y
,每意味着f
,f(x)=f(y)
。该要求在Haskell的以下插图中没有体现。(我们重写==
以创建分区类型,但是我们的函数定义可以随意忽略它并这样做。)
我的问题是,是否可以以一种可以进行逻辑测试的方式来实际陈述参照透明性,而Haskell是否真的支持这一逻辑主张?
module Main (main) where
data Floop = One | Two | Three
instance Eq Floop where
One== One= True
One== Two = False
One== Three = False
Two == One= False
Two == Two = True
Two == Three = True --- 2=3
Three == One= False
Three == Two = True --- 3=2
Three == Three = True
shuffle :: Floop -> Floop
shuffle One= Two
shuffle Two = Two --- fix 2
shuffle Three = One --- move 3
main = print ( (Two == Three) && (shuffle Two /= shuffle Three) )
--- prints "True" proving Haskell violates Leibniz Law
Robin Zigmon.. 7
略微扩展我在评论中已经说过的内容(感谢@FyodorSolkin提供的产品):
您并没有违反参照透明性,只是做了一个病理Eq
实例。
正如您所观察到的,该语言不会禁止您执行此操作,也不会禁止任何人将其设为非法Functor
或Monad
实例。(因为在实践中尝试检查这些定律是完全不可行的。)但是,仅仅因为某些事情不会引起编译器错误,并不必然意味着这样做是正确的。
因此,您的示例的问题在于,尽管从语义上来说,(==)
在Haskell中确实意味着“相等”,但它只是一个函数,实际上是类型类的方法-因此可以根据需要实现。没有什么可以阻止我进行定义,例如:
instance (Eq) (a -> b) where _ == _ = True
在此定义下,突然之间所有功能将被视为“相等”。如果我们认为这是平等的真正定义,则显然会违反参照透明性。我的意思是,事实并非如此。实际上,对于不是功能或以其他方式依赖或“包含”功能类型的任何类型,“相等”的含义很明显。(实际上,功能相等的含义也很明显,没有通用的算法来确定两个任意功能是否相等是完全不可能的。)
[编辑:我只是记得谈论IO
行动平等也没有多大意义。可能还存在其他类似的抽象类型,其中没有明确定义相等的含义。]
暂时迷恋抽象数学:您的Eq
实例肯定定义了一个等价关系,该关系被认为是一种“广义等式”-如果您使用该关系来创建等价类,则确实是等价的。但是,然后尝试将函数应用于这样的域/类型,这在同等类的不同元素上有所不同,这是胡说八道。实际上,从根本上说,这样的事情根本不是一个定义明确的数学函数,因为您在单个元素上定义它的方式不遵守等价关系。
略微扩展我在评论中已经说过的内容(感谢@FyodorSolkin提供的产品):
您并没有违反参照透明性,只是做了一个病理Eq
实例。
正如您所观察到的,该语言不会禁止您执行此操作,也不会禁止任何人将其设为非法Functor
或Monad
实例。(因为在实践中尝试检查这些定律是完全不可行的。)但是,仅仅因为某些事情不会引起编译器错误,并不必然意味着这样做是正确的。
因此,您的示例的问题在于,尽管从语义上来说,(==)
在Haskell中确实意味着“相等”,但它只是一个函数,实际上是类型类的方法-因此可以根据需要实现。没有什么可以阻止我进行定义,例如:
instance (Eq) (a -> b) where _ == _ = True
在此定义下,突然之间所有功能将被视为“相等”。如果我们认为这是平等的真正定义,则显然会违反参照透明性。我的意思是,事实并非如此。实际上,对于不是功能或以其他方式依赖或“包含”功能类型的任何类型,“相等”的含义很明显。(实际上,功能相等的含义也很明显,没有通用的算法来确定两个任意功能是否相等是完全不可能的。)
[编辑:我只是记得谈论IO
行动平等也没有多大意义。可能还存在其他类似的抽象类型,其中没有明确定义相等的含义。]
暂时迷恋抽象数学:您的Eq
实例肯定定义了一个等价关系,该关系被认为是一种“广义等式”-如果您使用该关系来创建等价类,则确实是等价的。但是,然后尝试将函数应用于这样的域/类型,这在同等类的不同元素上有所不同,这是胡说八道。实际上,从根本上说,这样的事情根本不是一个定义明确的数学函数,因为您在单个元素上定义它的方式不遵守等价关系。
f(x)=f(x)
每x
绝不是重言式。在许多流行的语言中,此属性不成立。考虑一下Java,例如:
import java.util.*; public class Transparency { static int f(List xs) { xs.add(xs.size()); return xs.size(); } public static void main(String[] args) { List x = new ArrayList<>(); System.out.println("Is java referentially transparent? " + (f(x) == f(x))); } } $ javac Transparency.java $ java Transparency Is java referentially transparent? false
在这里,由于将f
其输入突变x
,如果将x的定义替换为f(x) == f(x)
:则将改变行为,这f(new ArrayList<>()) == f(new ArrayList<>())
实际上是对的,但是当使用变量来减少重复项时,它的值为false。在Haskell中,这样的替换总是有效的(不考虑诸如作弊unsafePerformIO
)。