Dealing with configuration data
Hal Daume III
hdaume@ISI.EDU
Wed, 25 Sep 2002 16:34:02 -0700 (PDT)
I don't mean to troll, but this isn't what I meant. Suppose we have:
data Configuration = ... -- config data
globalConfig :: IORef Configuration
globalConfig = unsafePerformIO (newIORef undefined)
Now, we define an unsafe function to read the configuration:
getConfig :: Configuration
getConfig = unsafePerformIO $ readIORef globalConfig
Okay, this is "bad" but I claim it's okay, iff it is used as in:
main = do
...read configuration from file...no calls to getConfig...
writeIORef globalConfig configuration
doStuff
return ()
now, we have doStuff :: IO a. doStuff is allowed (even in its pure
methods) to use getConfig. I claim that this is safe. I could be
wrong; this is only a hand-waiving argument. Why?
The first reference in the program to globalConfig is through a
writeIORef. This means that at this point globalConfig gets evaluated and
thus a ref is created. Immediately we put a value in it.
Now, when doStuff runs, since it is an action run *after* the call to
writeIORef, provided that it doesn't also write to 'globalConfig' (which I
mentioned in my original message), any call to getConfig is deterministic.
I could be wrong...please correct me if I am.
--
Hal Daume III
"Computer science is no more about computers | hdaume@isi.edu
than astronomy is about telescopes." -Dijkstra | www.isi.edu/~hdaume
On Thu, 26 Sep 2002, Nick Name wrote:
> On Wed, 25 Sep 2002 16:06:29 -0700 (PDT)
> Hal Daume III <hdaume@ISI.EDU> wrote:
>
> > I don't feel bad about doing
> > this because GHC does this itself for its own configuration :).
>
> I am going to show you that using unsafePerformIO where there really are
> side effects leads to unpredictable results, and is generally wrong in a
> lazy language. Don't hate me for this :)
>
> Consider this example (supposing that a Config is represented by an
> Int):
>
> storeConfig :: Int -> ()
> readConfig :: Int
>
> They both are obtained through the use of "unsafePerformIO".
>
> Now, say I got this code:
>
> (storeConfig 0,storeConfig 1,readConfig,storeConfig 0,readConfig)
>
> What is this 5-uple supposed to evaluate to?
>
> First of all, this depends on order of evaluation. We can't say that all
> the elements of the tuple will be evaluated, so we can't tell if the
> fifth readConfig will evaluate to 0 or 1 (if the third storeConfig is
> never evaluated, readConfig will evaluate to 0, else to 1) This is one
> of the causes of the use of monads: ensuring correct order of
> evaluation.
>
> Second, suppose we were able to force order of evaluation (which
> shouldn't be allowed, in a lazy language). We still can't say what the
> last "readConfig" would evaluate to, since we don't know if the compiler
> is substituting equals for equals (I am expecting a lazy functional
> language to do this).
>
> If the compiler does, the last readConfig is equal to the first (in
> fact, by the use of unsafePerformIO, you have told the compiler that
> both the functions storeConfig and readConfig are pure, which is not
> true) and will evaluate to 1, else it will evaluate to 0. And, besides,
> the compiler should also substitute the second "storeConfig 0" with the
> result of the first occurrence, so it would not evaluate the second
> "storeConfig" at all.
>
> This is another example of the need for monads: allowing program
> transformations, first of all substituting equals for equals.
>
> This is why (even if, by enough knoweledge of the implementation, we
> could), by only relying on the semantics of a lazy language, we can not
> have functions with side effects.
>
> If it wasn't so, they would not have invented monads, believe me.
>
> I apologize, as always, for my terrible english, and hope I have been
> clear.
>
> Vincenzo Ciancia
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe@haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>