我刚刚了解了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"
Run Code Online (Sandbox Code Playgroud)
非常简单,但是当我runhaskell使用GHC 7.8.4时,我得到了错误的结果.一个更简单的案例怎么样?我们试试这个:
-- 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"
Run Code Online (Sandbox Code Playgroud)
我仍然得到False的结果.我可以eqStableName返回的唯一方法True是当我调用makeStableName相同的精确绑定变量时.像这样:
-- in this example, r can be anything
a <- makeStableName r
b <- makeStableName r
print (a `eqStableName` b)
Run Code Online (Sandbox Code Playgroud)
但这不再有用.我已经知道每个表达式都等于它自己,所以这并没有给我任何新的信息.我的问题是双重的:
StablePointer旨在满足?StablePointer.我知道它会给出假阴性,但在什么情况下我可以期望它们总是会发生?感谢您的任何见解.非常感谢他们.
- 编辑 -
我发现如果我用它ghc代替它runhaskell,那么实例2确实表明它们是相同的.示例1仍然失败.问题仍然存在.
那些回归的原因False可能是懒惰.在GHC中,m并且n将引用不同的thunk,因为它们尚未被评估.makeStableName不强迫价值.如果您手动强制thunk,它们将是相同的:
let m = Wrapper myFunc
a <- makeStableName $! getWrapper m
b <- makeStableName $! getWrapper m
print (a `eqStableName` b)
Run Code Online (Sandbox Code Playgroud)
这打印True($!将强制返回getWrapperWHNF 的值).
请注意,如果您不使用runhaskell而是使用编译-O1,GHC将实际编译此代码以便打印True.从核心看,似乎GHC所做的是内联m,getWrapper因此运行的代码实际上是这样的:
a <- makeStableName myFunc
b <- makeStableName myFunc
Run Code Online (Sandbox Code Playgroud)
然后当然会生成相同的稳定指针.
因此,如果您希望获得最大的相等性,请始终在为其提供稳定指针之前强制使用您的值 虽然如果两个值相等,则它们被赋予相同的稳定指针,但是没有保证.
如果您尚未阅读,我还建议阅读Stretching the storage manager,它解释了稳定指针的实现.