RE: unsafePtrCompare, anybody?

One's intuition would suggest that you could be safely implement mkAtom without wrapping it in a IO monad. After all, at least at a abstract level, an atom table is referentially transparent. The library documentation says that lack of side effects and environmental independance is sufficent to order for uses of unsafePerformIO to be safe. Is there a exact (or at least better) criterion for safety?
An exact criterion would require a formal semantics for Haskell, which we don't have. But informally, if a function which is implemented using unsafePerformIO is pure - that is, its result depends only on the values of its arguments and possibly its free variables - then that is a "safe" use of unsafePerformIO.
"unsafePerformIO" is used in the implementation of mkFastString, so how is it's side effects "safe".
It is safe because the side effects aren't visible outside the implementation of mkFastString. HTH, Simon

An exact criterion would require a formal semantics for Haskell, which we don't have. But informally, if a function which is implemented using unsafePerformIO is pure - that is, its result depends only on the values of its arguments and possibly its free variables - then that is a "safe" use of unsafePerformIO.
I'm not sure I completely understand you here. Obviously, these free variables must also include mutable variables, as mkFastString employs these, and the side-effects can be seen from within the module itself.
"unsafePerformIO" is used in the implementation of mkFastString, so how is it's side effects "safe".
It is safe because the side effects aren't visible outside the implementation of mkFastString.
My intuition says that none of the side-effects in my implementation are visible from the abstract level of the module. However, the use of unsafePerformIO certainly modifies the behaviour of the module. For example, the following definitions at the beginning of a GHCi session on the attached code lead to the subsequent behaviour: foo1 <- return (unsafePerformIO (mkAtom "foo")) foo2 <- return (unsafePerformIO (mkAtom "foo")) bar <- return (unsafePerformIO (mkAtom "bar")) safefoo1 <- mkAtom "foo" safefoo2 <- mkAtom "foo" safebar <- mkAtom "bar" list <- return [safefoo1, safefoo2, safebar, foo1, foo2, bar] Atom> [x == y| x <- list, y <- list] [True ,True ,False,False,False,False, True ,True ,False,False,False,False, False,False,True ,False,False,False, False,False,False,True ,False,False, False,False,False,False,True ,False, False,False,False,False,False,True ] As can be seen from above, the only times that (==) returns true, aside from reflexive cases, is when (safefoo1 == safefoo2) and (safefoo2 == safefoo1). I've also played with the order of atom creation, and it doesn't appear to have any effect on the truth table. best, leon

Leon Smith
My intuition says that none of the side-effects in my implementation are visible from the abstract level of the module. However, the use of unsafePerformIO certainly modifies the behaviour of the module.
Here's a simple test you can apply: Can you rewrite your module in pure Haskell so that it has the same observed behaviour but (probably) different operational behaviour (e.g. space and time usage)? If the answer is yes, then you preserve referential transparency, type safety and other desirable properties (on the assumption that pure Haskell has these properties). If the answer is no, you've probably broken one or more important properties. (There may still be hope but you might have to get very devious (cf. non-determinism in Rittri et al.'s UniqueSupply, Hughes and O'Donnell's non-determism monad and exception handling).) -- Alastair Reid reid@cs.utah.edu http://www.cs.utah.edu/~reid/
participants (3)
-
Alastair David Reid
-
Leon Smith
-
Simon Marlow