data StableName a
Stable names have the following property: If sn1 :: StableName and sn2 :: StableName and sn1 == sn2 then sn1 and sn2 were create
Holding the StableName of an object doesn't prevent it from being garbage collected, whereas holding the object itself around (to use with reallyUnsafePtrEquality# later) does. Sure, you can use System.Mem.Weak, but at that point, why not just use a StableName? (In fact, weak pointers were added with StableNames.)
Being able to hash them is the main motivator for StableNames, as the documentation says:
We can't build a hash table using the address of the object as the key, because objects get moved around by the garbage collector, meaning a re-hash would be necessary after every garbage collection.
In general, if StableNames will work for your purposes, I'd use them, even if you need to use unsafePerformIO; if you really need reallyUnsafePtrEquality#, you'll know. The only example I can think of where reallyUnsafePtrEquality# would work and StableNames wouldn't is speeding up an expensive Eq instance:
x == y =
x `seq` y `seq`
case reallyUnsafePtrEquality# x y of
1# -> True
_ -> slowEq x y
There's probably other examples I just haven't thought of, but they're not common.