I just learned about GHC's StablePointer
feature, which is really cool, but I can't figure out why it has won't show things as equal. Here is my test case:
-- 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"
Pretty simple, but when I do runhaskell
with GHC 7.8.4, I get a result of false. What about a simpler case? Let's try this:
-- 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"
I still get a result of False. The only way I can get eqStableName
to return True
is when I call makeStableName
on the same exact bound variable. Like this:
-- in this example, r can be anything
a <- makeStableName r
b <- makeStableName r
print (a `eqStableName` b)
But this is no longer helpful. I already know that every expression is equal to itself, so this doesn't give me any new information. My question is twofold:
StablePointer
intended to satisfy?StablePointer
. I know that it gives false negatives, but under what circumstances can I expect these to always occur?Thanks for any insights. They are much appreciated.
-- EDIT --
I discovered that if I build it with ghc
instead of runhaskell
, then Example 2 actually does show that they are equal. Example 1 still fails. The question still stands.
The reason those return False
is probably laziness. In GHC, m
and n
will refer to different thunks, since they are not evaluated yet. makeStableName
does not force the value. If you manually force the thunk, they will be the same:
let m = Wrapper myFunc
a <- makeStableName $! getWrapper m
b <- makeStableName $! getWrapper m
print (a `eqStableName` b)
This prints True
(The $!
will force the value returned by getWrapper
to WHNF).
Note that if you don't use runhaskell
but instead compile with -O1
, GHC will actually compile this code such that it prints True
. From looking at the Core, it seems that what GHC has done is inlining m
and getWrapper
so that the code that is run is effectively this:
a <- makeStableName myFunc
b <- makeStableName myFunc
Which then of course generates the same stable pointer.
So if you want maximum equality, always force your values before making stable pointers to them. There is no guarrante though that if two values are equal that they are assigned equal stable pointers.
If you haven't read it yet, I also recommend reading Stretching the storage manager which explains the implementation of stable pointers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With