Haskell 语言在引用透明度方面提供的确切承诺/保证是什么?至少 Haskell 报告没有提到这个概念。
Haskell 不提供准确的承诺或保证。存在许多类似unsafePerformIO
或traceShow
不具有引用透明性的功能。然而,名为Safe Haskell的扩展提供了以下承诺:
引用透明性——安全语言中的函数是确定性的,评估它们不会产生任何副作用。IO monad 中的函数仍然被允许并且像往常一样运行。但是,根据其类型,任何纯函数都可以保证确实是纯函数。此属性允许安全语言的用户信任这些类型。例如,这意味着 unsafePerformIO :: IO a -> a 函数在安全语言中是不允许的。
Haskell 在此之外提供了一个非正式的承诺:Prelude 和基础库往往没有副作用,而 Haskell 程序员倾向于将带有副作用的东西标记为此类。
显然,这个表达式现在在引用上是不透明的。我如何判断一个程序是否会受到这种行为的影响?我可以用 :: 来淹没程序,但这并不能使它变得非常可读。我想念的还有其他类别的 Haskell 程序吗?那是在完全注释和未注释之间?
正如其他人所说,问题来自这种行为:
Prelude> ( (7^7^7`mod`5`mod`2)==1, [False,True]!!(7^7^7`mod`5`mod`2) )
(True,False)
Prelude> 7^7^7`mod`5`mod`2 :: Integer
1
Prelude> 7^7^7`mod`5`mod`2 :: Int
0
发生这种情况是因为7^7^7
一个巨大的数字(大约 700,000 个十进制数字)很容易溢出 64 位Int
类型,但问题在 32 位系统上无法重现:
Prelude> :m + Data.Int
Prelude Data.Int> 7^7^7 :: Int64
-3568518334133427593
Prelude Data.Int> 7^7^7 :: Int32
1602364023
Prelude Data.Int> 7^7^7 :: Int16
8823
如果使用rem (7^7^7) 5
Int64 的余数将被报告为-3
但是因为 -3 相当于 +2 模 5,所以mod
报告 +2。
由于类Integer
的默认规则,答案在左侧使用Integral
;由于. Int
_ (!!) :: [a] -> Int -> a
如果您使用适当的索引运算符来Integral a
代替获得一致的东西:
Prelude> :m + Data.List
Prelude Data.List> ((7^7^7`mod`5`mod`2) == 1, genericIndex [False,True] (7^7^7`mod`5`mod`2))
(True,True)
这里的问题不在于引用透明性,因为我们调用的函数^
实际上是两个不同的函数(因为它们具有不同的类型)。让你感到困惑的是类型类,它是Haskell中约束歧义的一种实现;您已经发现这种歧义(与不受约束的歧义不同——即参数类型)会产生违反直觉的结果。这不应该太令人惊讶,但有时肯定有点奇怪。