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 "_"

ではなかなかよくわからない挙動をしてくれた。

*1:case文の場合小文字だと変数の束縛と区別できない

*2:もしくはUnknown

*3:===はMathematicaではシンボルに関する等号。x, yが未定義のとき x === y はFalseになる

*4:英語として間違っていると思う

*5:freeStablePtrしてないのでメモリリークする