我刚刚了解了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
时,我得到了错误的结果。一个更简单的案例怎么样?我们试试这个:
-- 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"
我仍然得到False的结果。我可以eqStableName
返回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仍然失败。问题仍然存在。
答案 0 :(得分:8)
返回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
返回给WHNF的值。
请注意,如果您不使用runhaskell
,而是使用-O1
进行编译,GHC将实际编译此代码,以便打印True
。从核心看,似乎GHC所做的是内联m
和getWrapper
,以便运行的代码实际上是这样的:
a <- makeStableName myFunc
b <- makeStableName myFunc
然后当然会生成相同的稳定指针。
因此,如果你想要最大程度的相等,那么在为它们提供稳定的指针之前总是强制你的值。但是没有保证,如果两个值相等,则它们被赋予相同的稳定指针。
如果您还没有阅读,我还建议阅读Stretching the storage manager,它解释了稳定指针的实现。