StableName は、あなたのような問題を解決するために特別に設計されています。
StableName はIO
モナドでのみ作成できることに注意してください。つまり、モナドでオブジェクトを作成するか、実装で使用するか(この状況では多かれ少なかれ問題ありません) の 2 つの選択肢があります。IO
unsafePerformIO
(==)
unsafe*
しかし、完全に安全な方法 (関数なし) でこれを行うことが可能であることを強調しておく必要がIO
あります。その後、完全に純粋な方法でそれらを比較できます。
例えば
data SNWrapper a = SNW !a !(StableName a)
snwrap :: a -> IO (SNWrapper a)
snwrap a = SNW a <$> makeStableName a
instance Eq a => Eq (SNWrapper a) where
(SNW a sna) (SNW b snb) = sna == snb || a == b
安定した名前の比較で「いいえ」と表示された場合でも、決定的な答えを得るには完全な値の比較を実行する必要があることに注意してください。
私の経験では、多くの共有があり、何らかの理由で共有を示すために他の方法を使用したくない場合に、かなりうまく機能しました。
(他の方法について言えば、たとえば、IO
モナドをモナドに置き換えて、そのモナドでState Integer
一意の整数を「安定した名前」に相当するものとして生成することができます。)
別のトリックは、再帰的なデータ構造がある場合、再帰をSNWrapper
. たとえば、代わりに
data Tree a = Bin (Tree a) (Tree a) | Leaf a
type WrappedTree a = SNWrapper (Tree a)
使用する
data Tree a = Bin (WrappedTree a) (WrappedTree a) | Leaf a
type WrappedTree a = SNWrapper (Tree a)
このように、短絡が最上層で発火しなくても、中間のどこかで発火する可能性があり、それでもいくらかの作業を節約できます。