Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GHC StablePointer equality reasoning

Tags:

haskell

ghc

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:

  1. What use cases is StablePointer intended to satisfy?
  2. How can we reason about the equality of 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.

like image 822
Andrew Thaddeus Martin Avatar asked Apr 15 '15 14:04

Andrew Thaddeus Martin


1 Answers

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.

like image 69
bennofs Avatar answered Oct 31 '22 22:10

bennofs