Haskell 言語が参照透過性に関して提供する正確な約束/保証は何ですか? 少なくとも Haskell のレポートでは、この概念について言及されていません。
Haskell は正確な約束や保証を提供しません。unsafePerformIO
orのような、traceShow
参照透過でない関数が多数存在します。ただし、 Safe Haskellと呼ばれる拡張機能は、次の約束を提供します。
参照透過性 — 安全な言語の関数は決定論的であり、それらを評価しても副作用は発生しません。IO モナド内の関数は引き続き許可され、通常どおりに動作します。ただし、純粋な関数は、その型に応じて、実際に純粋であることが保証されています。このプロパティにより、安全な言語のユーザーは型を信頼できます。これは、たとえば、 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
は、64 ビット型では簡単にオーバーフローする膨大な数 (10 進数で約 700,000 桁)が原因で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 は 5 を法とする +2 と同等であるため、+2 と報告されmod
ます。
クラスの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)
ここでの問題は、参照の透過性ではありません^
。なぜなら、呼び出している関数は実際には2 つの異なる関数(型が異なるため) だからです。つまずいたのは型クラスです。これは、Haskellで制約付きのあいまいさを実装したものです。このあいまいさ (制約のないあいまいさ、つまりパラメトリック型とは異なり) が直感に反する結果をもたらす可能性があることを発見しました。これはそれほど驚くべきことではありませんが、確かに少し奇妙です。