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)
但这不再有帮助。我已经知道每个表达式都等于它自己,所以这不会给我任何新信息。我的问题是双重的:
StablePointer
旨在满足哪些用例?
- 我们如何推理
StablePointer
的相等性。我知道它会产生假阴性,但在什么情况下我可以预期这些总是会发生?
感谢您的任何见解。非常感谢他们。
-- 编辑--
我发现如果我用 ghc
而不是 runhaskell
构建它,那么示例 2 实际上确实表明它们是相等的。示例 1 仍然失败。问题依旧。
那些 return False
的原因可能是懒惰。在 GHC 中,m
和 n
将引用不同的 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 所做的似乎是内联 m
和 getWrapper
,因此 运行 的代码实际上是这样的:
a <- makeStableName myFunc
b <- makeStableName myFunc
这当然会生成相同的稳定指针。
因此,如果您想要最大程度的平等,请始终在为它们创建稳定指针之前强制您的值。但是,如果两个值相等,则不能保证为它们分配了相等的稳定指针。
如果你还没有读过,我也推荐阅读Stretching the storage manager,它解释了稳定指针的实现。
我刚刚了解了 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)
但这不再有帮助。我已经知道每个表达式都等于它自己,所以这不会给我任何新信息。我的问题是双重的:
StablePointer
旨在满足哪些用例?- 我们如何推理
StablePointer
的相等性。我知道它会产生假阴性,但在什么情况下我可以预期这些总是会发生?
感谢您的任何见解。非常感谢他们。
-- 编辑--
我发现如果我用 ghc
而不是 runhaskell
构建它,那么示例 2 实际上确实表明它们是相等的。示例 1 仍然失败。问题依旧。
那些 return False
的原因可能是懒惰。在 GHC 中,m
和 n
将引用不同的 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 所做的似乎是内联 m
和 getWrapper
,因此 运行 的代码实际上是这样的:
a <- makeStableName myFunc
b <- makeStableName myFunc
这当然会生成相同的稳定指针。
因此,如果您想要最大程度的平等,请始终在为它们创建稳定指针之前强制您的值。但是,如果两个值相等,则不能保证为它们分配了相等的稳定指针。
如果你还没有读过,我也推荐阅读Stretching the storage manager,它解释了稳定指针的实现。