Dealing with configuration data

Koen Claessen koen@cs.chalmers.se
Thu, 26 Sep 2002 16:02:01 +0200 (MET DST)


Nick Name wrote:

 | The first idea from Koen Claessen (getConfig operates
 | directly on the file with unsafePerformIO) appears to
 | work, but this time we are *relying* on the fact that
 | the function will be evaluated once, since the file
 | could change and multiple evaluations of readConfig
 | wouldn't lead to the same result. This is not good
 | anyway.

But Hal Daume III's suggestion has that same problem:

 | data Configuration = ...  -- config data
 |
 | globalConfig :: IORef Configuration
 | globalConfig = unsafePerformIO (newIORef undefined)
 :
 | getConfig :: Configuration
 | getConfig = unsafePerformIO $ readIORef globalConfig
 :
 | main = do
 |    ...read configuration from file...no calls to getConfig...
 |    writeIORef globalConfig configuration
 |    doStuff
 |    return ()

Imagine "globalConfig" being evaluated twice! This means
there will be *two* IORefs in your program; one might be
initialized, and not the other. This is even more
disastrous.

(To see what could happen: just inline the definition of
globalConfig into the two places where it is used.)

This is why one has to be EXTREMELY careful when using these
kinds of constructs. Really, only use unsafePerformIO when
you know what you are doing, and otherwise, leave it to
someone else who can wrap it up into a nice, pure library.

In general, when using unsafePerformIO in this way, one
wants to tell the compiler that it is not allowed to inline
the expression. This can be done in most compilers by giving
compiler pragma's.

/Koen.

--
Koen Claessen
http://www.cs.chalmers.se/~koen
Chalmers University, Gothenburg, Sweden.