Prof. Legolasov
Prof. Legolasov

Reputation: 689

Haskell: preventing data modification

I have the following code which does the job of changing the configuration file:

mutateConfig :: (Config -> Config) -> IO ()
mutateConfig f = do
    !cfg <- readConfig
    let !newCfg = f cfg
     in writeConfig newCfg

Bang patterns are used to disable lazy evaluation: i need writeConfig to be called after an old configuration was parsed and modified with function f.

But there is still a problem: imagine that f gives an error. In this case writeConfig has already opened the file for writing (because it is called first by lazy evaluation) when an error occures, so the configuration file gets lost.

I have tried to compose a simple step-by-step logic from IO monads (in order to prevent the loss of data) like this

!cfg <- readConfig
newCfg <- return $ f cfg
writeConfig newCfg

but this doesn't work either (I kind of expected that).

What would be the correct way to accomplish this goal?

Upvotes: 1

Views: 83

Answers (1)

melpomene
melpomene

Reputation: 85767

If Config is completely strict (no embedded bottoms within), you can do this:

do
    cfg <- readConfig
    let newCfg = f cfg
    evaluate newCfg
    writeConfig newCfg

If it's not, you could do something like evaluate (deepseqnewCfg ()).

But there's another problem: In general, writing a file can fail due to I/O errors (for example because the disk is full). To avoid this, you can write to a temporary file and rename it to the real target at the end. Doing it this way guarantees you only overwrite the config if there were no errors during the writing.

Upvotes: 4

Related Questions