haskellexceptionclassy-prelude

How to write a "retryForever" function in Haskell using exception handling?


I'd like to create a function that can recover from as many errors as is reasonable, and try again. Of course, meaningful handling of errors is covered in other parts of the program - this is a last ditch effort to keep things running. So I wrote this:

retryForever prog = catchAny prog progRetry
  where
    progRetry :: SomeException -> m a
    progRetry ex = do
      putStrLn $ pack $ show ex
      threadDelay 4000
      prog

Then I wrap my main IO action in retryForever:

main :: IO ()
main = retryForever $ do
  logger <- newLogger
  -- ...

In another part of my program (likely a different green thread), I test this with:

error "this is a TEST ERROR"

Resulting in:

: this is a TEST ERROR
CallStack (from HasCallStack):
  error, called at XXXX

(and the program dies instead of continuing on)

Note that I'm using classy-prelude, for the cases where that may matter, e.g. catchAny doesn't handle asynchronous exceptions in there, which may well be the issue here.


Solution

  • When the program failed, you should run the program prog again, but wrapped in retryForever such that if it fails again, you thus keep trying:

    import Control.Monad.Catch(catchAll)
    
    retryForever :: IO a -> IO a
    retryForever prog = catchAll prog retry
      where retry ex = do
          putStrLn $ pack $ show ex
          threadDelay 4000
          retryForever prog