Haskell GHC中导出的Eq实例的效率如何?

Haskell GHC中导出的Eq实例的效率如何?,haskell,lazy-evaluation,Haskell,Lazy Evaluation,当我比较数据类型的同一实例时,GHC(通常是Haskell)派生的Eq实例是否会触发短路 -- will this fire? let same = complex == complex 我的计划是读入一个惰性数据结构(比如树),更改一些值,然后比较新旧版本,创建一个diff,然后将其写回文件 如果存在内置短路,则当发现新结构引用旧值时,比较步骤将立即中断。同时,这不会从文件中读取超过必要的内容 我知道我不应该担心Haskell中的引用,但这似乎是处理延迟文件更改的好方法。如果没有内置的短路电

当我比较数据类型的同一实例时,GHC(通常是Haskell)派生的
Eq
实例是否会触发短路

-- will this fire?
let same = complex == complex
我的计划是读入一个惰性数据结构(比如树),更改一些值,然后比较新旧版本,创建一个diff,然后将其写回文件

如果存在内置短路,则当发现新结构引用旧值时,比较步骤将立即中断。同时,这不会从文件中读取超过必要的内容


我知道我不应该担心Haskell中的引用,但这似乎是处理延迟文件更改的好方法。如果没有内置的短路电路,有没有实现的方法?欢迎对不同方案提出建议。

(==)
的两个参数是同一个对象时,不会发生短路。派生的
Eq
实例将进行结构比较,在相等的情况下,当然需要遍历整个结构。您可以使用

GHC.Prim.reallyUnsafePtrEquality# :: a -> a -> GHC.Prim.Int#
但事实上,这种情况很少发生:

Prelude GHC.Base> let x = "foo"
Prelude GHC.Base> I# (reallyUnsafePtrEquality# x x)
1
Prelude GHC.Base> I# (reallyUnsafePtrEquality# True True)
1
Prelude GHC.Base> I# (reallyUnsafePtrEquality# 3 3)
0
Prelude GHC.Base> I# (reallyUnsafePtrEquality# (3 :: Int) 3)
0
如果您从文件中读取一个结构,它肯定不会找到与内存中已经存在的对象相同的对象

您可以使用重写规则来避免对词汇相同的对象进行比较

module Equal where

{-# RULES
"==/same"  forall x. x == x = True
  #-}

main :: IO ()
main = let x = [1 :: Int .. 10] in print (x == x)
这导致了

$ ghc -O -ddump-rule-firings Equal.hs 
[1 of 1] Compiling Equal            ( Equal.hs, Equal.o )
Rule fired: Class op enumFromTo
Rule fired: ==/same
Rule fired: Class op show

规则触发(注意:它不是用
let x=“foo”
触发的,而是用用户定义的类型触发的)。

StableNames是专门为解决类似您的问题而设计的

请注意,StableNames只能在
IO
monad中创建。因此,您有两种选择:要么在
IO
monad中创建对象,要么在
(==)
实现中使用
unsafePerformIO
(在这种情况下,这或多或少是好的)

但我要强调的是,可以以完全安全的方式(没有
不安全的*
函数)实现这一点:只有在
IO
中才能创建稳定的名称;在那之后,你可以用一种纯粹的方式来比较它们

例如

使用


这样,即使短路不会在顶层发生火灾,它可能会在中间的某个地方发生火灾,并且仍然会为您节省一些工作。

如果您将哈希值附加到每个树节点,您可能会得到类似的短路,但我同意只能够比较引用(在某种程度上说,是由内存管理分配的散列!)在这里会更严格,更有效率+那你呢。我刚发现这件事。这对我有什么帮助吗?我认为词汇上的平等不会让我走得更远。我会调查一下真正不安全的行为(尽管真正不安全的行为让我害怕)。参考资料(希望)是相同的。我想在
树->树
函数之后比较两棵树。类型为
(Tree->Tree)->的内容会发生变化。我当然可以记录所有的变化,但是当我只需要比较参考资料时,我觉得效率很低。嗯。。。真正不安全的行为似乎很有可能失败。在每一个假阴性都会导致光盘读取错误的情况下。好吧,如果你将
趣味树
进行比较,那么
真正不安全的属性
检测到公共子树的可能性非零。但是,是的,您必须预期它通常不会检测到相等。也许您可以添加一段关于
StableName
?尽管如此,我还是会接受你的回答,但我刚刚读了一本书,StableNames似乎是一条出路。@Florian(和Daniel),它确实有效。使用一些
seq
magic:
let x=[1];y=1:x;y'=x`seq`y'`seq`I#(reallysunsafeptrequality#x y')中的尾部y
给出1。(假设它可以在一个更复杂的数据结构上工作,其中有不变的子结构。)这或多或少正是我现在实现的。所以我接受这个答案。
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
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)