I have a following working definition:
{-# LANGUAGE ScopedTypeVariables #-}
module Control.Retry where
import Prelude hiding (catch)
import Control.Exception
import Control.Concurrent
retrying [] action = action 
retrying (i:is) action = catch action processError
  where 
    processError (e :: IOException) = threadDelay i >> retrying is action
Just out of curiosity I am wondering how I could reimplement this without utilizing the ScopedTypeVariables pragma, or whether I could at all, and what the inferred type declaration of processError actually is, because specifying processError :: IOException -> IO a makes it uncompilable.
Another option, maybe a little cleaner than asTypeOf:
retrying [] action = action 
retrying (i:is) action = catch action processError                                                                                                                                
  where
    processError e = threadDelay i >> retrying is action
      where
        _ = e :: IOException
Not sure if this is idiomatic; I just made it up and it worked.
If you want to avoid ScopedTypeVariables, you can most of the time use asTypeOf.
retrying [] action = action 
retrying (i:is) action = catch action processError
  where 
    processError e = snd (e `asTypeOf` (undefined :: IOException), threadDelay i >> retrying is action)
The undefined :: IOException is an expression type signature, and that is allowed by the standard. The asTypeOf requires the exception e to be an IOException.
I would prefer ScopedTypeVariables here, though.
With
retrying :: [Int] -> IO a -> IO a
the type of processError is inferred as
processError :: IOException -> IO a
with the a here being the same type variable as in the signature of retrying. That type can, however, not be specified in Haskell without the ScopedTypeVariables extension, since type variables in written-down signatures are by default universally quantified.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With