Global variables?
Jon Cast
jcast@ou.edu
Sun, 02 Feb 2003 21:11:56 -0600
Claus Reinke <claus.reinke@talk21.com> wrote:
> > > > import IORef
> > > > import IOExts
> > > >
> > > > globalVar :: IORef Int
> > > > globalVar = unsafePerformIO $ newIORef 0
> > > John Hughes wrote a nice pearl on the subject, see
> > > http://www.math.chalmers.se/~rjmh/Globals.ps
> > This paper claims ``unsafePerformIO is unsafe''. That's not
> > actually true in the sense meant; unsafePerformIO merely has safety
> > pre-conditions that the compiler can't check.
> Which is the main sense in which the 'unsafe' prefix is usually meant
> to be interpreted, and that is bad enough (see below, then re-read
> John's quote of Simon PJ's description of unsafePerformIO;-). In
> particular, the 'unsafe'-prefix is not a hint for the implementation
> to treat something with extra care, but a hint for the programmer that
> the implementation may shake unsafe expressions around like any other
> ones (inlining, cse, ..), even though that is bound to lead to
> problems with the hidden side-effects.
So you have to be careful. If that weren't what I said, I'd disagree
with you :)
> It is the programmer's responsibility to verify that none of these
> problems matter in the particular case of usage. Since many advances
> in compiler technology tend to invalidate those verifications, it is
> almost impossible to guarantee safety in such cases
Even with sufficiently liberal use of {-# NOINLINE #-}?
> - about the best one can hope for is to identify and document
> precisely which assumptions need to be made to "guarantee"
> safety. Unfortunately, this leaves it to users to figure out whether
> the assumptions made by 'unsafe' authors (e.g., no inlining) are still
> valid at the point of use..
> Btw, when talking about unsafety in that paper, John also happens to
> point out the other little problem with unsafePerformIO: it permits to
> break type safety (many a good spirit has stumbled over that
> "polymorphic references" problem in other functional languages).
> > The precondition (proof obligation) of unsafePerformIO is that the
> > order in which unsafePerformIOs are performed cannot affect the
> > outcome of the program. However, in this case, ordering doesn't
> > matter: the only side effect is allocation of a new IORef, and
> > IORefs are sufficiently opaque we don't care (or really know) about
> > un-allocated IORefs while the only case we care about the
> > now-allocated IORef is when we de-reference it. But, that forces
> > the IORef, which executes the unsafePerformIO. So, whenever we
> > access the variable, it is allocated. Therefore, the outcome of the
> > program (regardless of the order of evaluation) is the same as if
> > all such global variable declarations are executed before main
> > begins executing. So, the outcome is independent of the order of
> > evaluation.
> > There you go: the precondition of unsafePerformIO is satisfied, so
> > the usage is safe.
> There you went.. into one of the many available traps in this
> mine-field:
> You argue that unallocated IORefs don't matter as long as "the" IORef
> is allocated before it is dereferenced. But that's just part of the
> problem - what about inlining globalVar, creating multiple IORefs?
{-# NOINLINE globalVar #-} :)
> Remember that, by using unsafePerformIO, you've given the compiler the
> license to treat globalVar as an expression without side-effects, and
> for those inlining is a common first step to enable further
> optimisations (last time I checked, the language report didn't even
> guarantee the sharing on which the globalVar trick depends)! Now there
> are multiple side-effects instead of a single one, and read-and
> write-accesses are spread out over the multiple copies of your IORef,
> none of which is likely to hold the value you'd like it to have..
> Cheers,
> Claus
Jon Cast