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