IORef keeping value if an error occurs

174 Views Asked by At

Is there a way to NOT modify IORef value if error happens inside modifyIORef?

import Data.IORef

main = do
  a <- newIORef (1::Int)

  -- a keeps error now, not Int
  modifyIORef a (const $ error "Error forever")

  val <- readIORef a  -- val is error now
  putStrLn $ "Result: " ++ show val -- throws error
2

There are 2 best solutions below

1
On

Haskell does not do error handling in the same way that other languages, such as Python and Java, do. When you call the error function, the program will halt. Period. The error cannot be caught. There is no way of redirecting it, or restarting the program. The error function raises an error, not an exception. If you want to represent the idea of failure in Haskell, you use the Maybe and Either monads. Below is how you might implement the functionality that you want using the Either monad.

main = do
  a <- NewIORef (Right 1 :: Either String Int)
  modifyIORef a (const $ Left "some execution error")  
  -- a now holds a "Left" value, representing an error
  val <- readIORef a 
  -- val now holds the "Left" value
  case val of
    Left err -> putStrLn $ "Error: " ++ err -- prints error (if applicable)
    Right val -> putStrLn $ "Result: " ++ show val -- prints value (if applicable)

EDIT: As dfeuer kindly pointed out in his comment, it is possible to intercept errors in GHC. It is, however, considered bad practice except in very specific circumstances, so using the Maybe and Either types is still preferred.

2
On

I believe you can do this with modifyIORef' or atomicModifyIORef' wrapped in an exception handler. I wouldn't recommend it in general; error calls produce strings for users to read, not detailed info for a handler. And the whole "imprecise exception" mechanism for handling exceptions thrown from "pure" code requires quite a lot of care to use correctly. You're almost always better off making sure never to throw exceptions from pure code. ExceptT and Either and such can help.