GHC StablePointer 相等推理

GHC StablePointer equality reasoning

我刚刚了解了 GHC 的 StablePointer 功能,这真的很酷,但我不明白为什么它不显示平等。这是我的测试用例:

-- Example 1
import System.Mem.StableName

data Wrapper = Wrapper { getWrapper :: Int -> Bool }

myFunc :: Int -> Bool
myFunc = (> 4)

main :: IO ()
main = do
  let m = Wrapper myFunc
  a <- makeStableName $ getWrapper m
  b <- makeStableName $ getWrapper m
  print (a `eqStableName` b)
  putStrLn "Done"

非常简单,但是当我使用 GHC 7.8.4 执行 runhaskell 时,我得到的结果是 false。更简单的情况呢?让我们试试这个:

-- Example 2
import System.Mem.StableName

main :: IO ()
main = do
  let m = (+2) :: Int -> Int
      n = m
  a <- makeStableName m
  b <- makeStableName n
  print (a `eqStableName` b)
  putStrLn "Done"

我仍然得到错误的结果。我可以获得 eqStableName 到 return True 的唯一方法是当我在同一个确切的绑定变量上调用 makeStableName 时。像这样:

  -- in this example, r can be anything
  a <- makeStableName r
  b <- makeStableName r
  print (a `eqStableName` b)

但这不再有帮助。我已经知道每个表达式都等于它自己,所以这不会给我任何新信息。我的问题是双重的:

  1. StablePointer 旨在满足哪些用例?
  2. 我们如何推理 StablePointer 的相等性。我知道它会产生假阴性,但在什么情况下我可以预期这些总是会发生?

感谢您的任何见解。非常感谢他们。

-- 编辑--

我发现如果我用 ghc 而不是 runhaskell 构建它,那么示例 2 实际上确实表明它们是相等的。示例 1 仍然失败。问题依旧。

那些 return False 的原因可能是懒惰。在 GHC 中,mn 将引用不同的 thunk,因为它们尚未被评估。 makeStableName 不强制值。如果你手动强制 thunk,它们将是相同的:

  let m = Wrapper myFunc
  a <- makeStableName $! getWrapper m
  b <- makeStableName $! getWrapper m
  print (a `eqStableName` b)

这会打印 True$! 会将 getWrapper 编辑的值 return 强制为 WHNF)。

请注意,如果您不使用 runhaskell 而是使用 -O1 进行编译,GHC 实际上会编译此代码以打印 True。从核心来看,GHC 所做的似乎是内联 mgetWrapper,因此 运行 的代码实际上是这样的:

a <- makeStableName myFunc
b <- makeStableName myFunc

这当然会生成相同的稳定指针。

因此,如果您想要最大程度的平等,请始终在为它们创建稳定指针之前强制您的值。但是,如果两个值相等,则不能保证为它们分配了相等的稳定指针。

如果你还没有读过,我也推荐阅读Stretching the storage manager,它解释了稳定指针的实现。