"global variables" and code optimization
Hi all, in a compiler project I am using "global variables" of the form: gNewLabelNr :: IORef Int gNewLabelNr = unsafePerformIO $ newIORef 1 getAndUpdateVar var f = unsafePerformIO $ do oldVal <- readIORef var writeIORef var (f oldVal) return oldVal makeBlockName = "Block_" ++ show (getAndUpdateVar gNewLabelNr (+1)) (I know I could use a Monad to avoid this kind of unsafe programming, but I don't (didn't) want to rewrite existing code.) The problem is, that with optimizations turned on (using ghc V6.2.1) the label number is calulated only once, so each call to makeBlockName yields the same value. I tried a) adding a dummy parameter to makeBlockName and b) specifying an INLINE pragma to both getAndUpdateVar and makeBlockName, but that doesn't help. What can I do to prevent optimization to optimize that calculation away? Thanks, Bernd
The problem is, that with optimizations turned on (using ghc V6.2.1) the label number is calulated only once, so each call to makeBlockName yields the same value. I tried a) adding a dummy parameter to makeBlockName and b) specifying an INLINE pragma to both getAndUpdateVar and makeBlockName, but that doesn't help. What can I do to prevent optimization to optimize that calculation away?
This is why it is called *unsafe* - code that contains unsafePerformIO is usually wrong, and the proof obligations on the programmer when using it are complex and ill-defined. There is a noinline pragma you can use, but I'm not at all sure it will save you in this specific situation. Consider: makeBlockName has type String; it's just a constant. How is the compiler going to give it a different value each time? What does "each time" mean, anyway? It's possible that by putting a noinline pragma on getAndUpdateVar you might arrange that each call to that gives a fresh label number; but you'll have to call it directly from your main function, rather than from within a constant as you have here. (note also that just adding an unused argument to makeBlockName probably won't help; GHC is cleverer than that). Why not just thread (newLabelNr :: Int) through your code to the places that need it? If you do this with other things too, then put them in a record. This is a kind of "poor-man's" state monad. --KW 8-) -- Keith Wansbrough <kw217@cl.cam.ac.uk> http://www.cl.cam.ac.uk/users/kw217/ University of Cambridge Computer Laboratory.
Keith Wansbrough wrote:
... [Bernd discusses his problems using unsafeperformIO to generate unique names, and Keith responds]
Why not just thread (newLabelNr :: Int) through your code to the places that need it? If you do this with other things too, then put them in a record. This is a kind of "poor-man's" state monad.
Alternatively, if you're threading something through your program, why not thread a splittable supply of new names? You can use the supply to generate fresh identifiers directly, and it all behaves purely functionally. There's code for unique supplies here: http://www.csg.csail.mit.edu/~earwig/haskell-lib/index.html Look down the page for "Arbitrary Splittable Supplies". -Jan-Willem Maessen
participants (3)
-
"Holzmüller, Bernd" -
Jan-Willem Maessen - Sun Labs East -
Keith Wansbrough