Function Equality (try)
関数はEqのインスタンスではないが…
Haskellでは関数はEqクラスに属さない。定義域が無限集合の場合には二つの関数が等しいことを有限の手続きでは証明できないためだ。それはまあいいのだが、
f = ... g = f
こういう場合でさえもfとgが等しいかどうかを判定することができない。
以下のようなコードが書ければ便利なことは多い*1。
func f = if f == func1 then ... else if f == func2 then ... func1 = ... func2 = ...
上記の場合に判定できないのは、数学的な制限のためではない。gの定義がfなのだからfとgが等しいのは自明である。
つまり、定義から等しい場合には真を返し、それ以外の場合には偽*2を返すようなクラス、
instance Identity a where (===) :: a -> a -> Bool
があっても論理的には問題はないはずだ*3。
しかし、実装的には困難がある。pointer equalityとしたいところだが、ポインタはGC時に動いてしまうので、追跡するか動かないようなGCを採用する必要がある。
"definitional"-equality*4をサポートするクラス
StablePtr(Foreigh.StablePtr)やStableName(System.Mem.StableName)は、この実装に使えそうに見えるが、どうなのだろうか?ドキュメント上ではGCに左右されないことは保証されているが、同じオブジェクトが等しいStablePtrやStableNameを生成することを保証しているという記述はない。
import Foreign.StablePtr import Control.Monad (liftM2) import System.IO.Unsafe (unsafePerformIO) class Identity a where (===) :: a -> a -> Bool f === g = unsafePerformIO (liftM2 (==) (newStablePtr f) (newStablePtr g)) instance Identity (a -> b) where func1 = id func2 x = sin x + cos x func2' = \x -> sin x + cos x
で試してみる*5。
*Main> func1 === func1 True *Main> func2 === func2 True
うまくいくかと思ったが、肝心の
func3 f = if f === func1 then "func1" else if f === func2 then "func2" else if f === func2' then "func2'" else "_" func3' f = f `seq` if f === func1 then "func1" else if f === func2 then "func2" else if f === func2' then "func2'" else "_"
ではなかなかよくわからない挙動をしてくれた。