StableNames in presence of class constraints / monadic functions
|Reported by:||ifigueroap||Owned by:|
|Type of failure:||None/Unknown||Test Case:|
|Related Tickets:||Differential Revisions:|
I'm trying to implement a notion of function equality using the StableNames API.
So far it has worked relatively well, but I'm facing the problem of getting negatives when functions have class constraints, like (Monad m).
I implemented de eq function as follows:
import System.Mem.StableName import Control.Monad.State eq :: a -> b -> IO Bool eq a b = do pa <- makeStableName a pb <- makeStableName b return (hashStableName pa == hashStableName pb)
And for testing the following function and main program:
successor :: (Num a, Monad m) => a -> m a successor n = return (n+1) main :: IO () main = do b1 <- eq (successor :: Int -> Maybe Int) (successor :: Int -> Maybe Int) b2 <- eq (successor :: Int -> State Int Int) (successor :: Int -> State Int Int) print (show b1 ++ " " ++ show b2)
Running the program in ghci, gives "False False". Somewhere on the internet there is a tip to use a compiled module with optimizations, using ghc --make -O File.hs. Doing this, I get "True True".
However, with a slight modification of main the result is "True False":
main :: IO () main = do b2 <- eq (successor :: Int -> State Int Int) (successor :: Int -> State Int Int) b1 <- eq (successor :: Int -> Maybe Int) (successor :: Int -> Maybe Int) print (show b1 ++ " " ++ show b2)
After reading a lots of forums and mail lists, it seems the problem is because of the dictionary-passing style: somehow StableNames see that the dictionaries are different and then returns false . Or maybe there are some closures created in background, that do not map to the same memory locations.
I know that doing a let/where binding, this particular example works, e.g.:
main :: IO () main = do b2 <- eq f2 f2 b1 <- eq f1 f1 print (show b1 ++ " " ++ show b2) where f1 = (successor :: Int -> Maybe Int) f2 = (successor :: Int -> State Int Int)
But in general it is cumbersome to do this way, and it is hard to anticipate whether the comparison is going to be "ok" or not. Another way I've found to solve the problem is to put very explicit type annotations, but this is problematic in monadic code that is intended to be generic.
I wonder if it is possible to implement some compiler flag to change the behavior of StableNames (or maybe a differente API?) so it returns true when the only difference is the dictionary?
In Scheme/Racket this notion of function equality is done tagging every function with a unique value, upon closure creation. Using the macro system this is done transparently for the programmer. I guess I could emulate this using a wrapper type for functions, and using GHC.Unique; but I want to see if there are other options. Maybe a compiler plugin can annotate functions in the same way? It seems with Template Haskell is not possible to intercept function definition.
Also, the example works correctly (returning "True True" in both cases) when the definition of eq is:
eq :: a -> b -> IO Bool eq a b = do unsafeCoerce b a pa <- makeStableName a pb <- makeStableName b return (hashStableName pa == hashStableName pb)
I guess it is because the dictionaries are somehow unified. But using this eq in general leads to (predictable) Bus errors.