Reputation: 13088
Original Title: How to deal with multiple instances of Exception types when inspecting all Exceptions?
I have the following imports (note my prelude is actually ClassyPrelude, which uses UnliftIO.Exception). Note that System.Logger is from tinylog, a thin library on top of fast-logger.
import Prelude hiding(log)
import System.Logger hiding(log)
import qualified System.Logger as TL
And the following function:
logExceptions :: MonadUnliftIO m => Logger -> m a -> m a
logExceptions logger program = withException program
(\ex -> do
logIt Warn logger ["Logging exception: ", (show ex)]
flush logger
)
Putting the lambda into a local function with a type may make it slightly more clear:
logExceptions :: MonadUnliftIO m => Logger -> m a -> m a
logExceptions logger program = withException program logEx
where
logEx :: (MonadUnliftIO m, Exception e) => e -> m ()
logEx ex = do
logIt Warn logger ["Logging exception: ", (show ex)]
flush logger
This results in the following compile error:
* Could not deduce (Exception e0)
arising from a use of `withException'
from the context: MonadUnliftIO m
bound by the type signature for:
logExceptions :: forall (m :: * -> *) a.
MonadUnliftIO m =>
Logger -> m a -> m a
at src/FDS/Logging.hs:19:1-56
The type variable `e0' is ambiguous
These potential instances exist:
instance Exception SomeException -- Defined in `GHC.Exception.Type'
instance Exception IOException -- Defined in `GHC.IO.Exception'
instance Exception SomeAsyncException
-- Defined in `GHC.IO.Exception'
...plus four others
...plus 30 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
* In the expression: withException program logEx
In an equation for `logExceptions':
logExceptions logger program
= withException program logEx
where
logEx :: (MonadUnliftIO m, Exception e) => e -> m ()
logEx ex
= do logIt Warn logger ...
....
|
20 | logExceptions logger program = withException program logEx
|
The most worrisome bit is the plus 30 instances involving out-of-scope types
. I could hide these imports to slightly improve the situation:
import GHC.Exception.Type hiding(SomeException)
import GHC.IO.Exception hiding(IOException, SomeAsyncException)
But it hardly seems reasonable to go through and find all 30+ exception types and mask them all out in this fashion. I assume I'm doing something quite wrong here, or do I really need to go through and mask everything?
Note:
logIt
function is just a thin wrapper around the log
function from tinylog - feel free to substitute with whatever feels ergonomic.Upvotes: 1
Views: 219
Reputation: 13088
I now understand my issue was that a concrete type was needed for the Exception
argument, since it is a polymorphic function as stated in my question, and there is no call site to narrow this down to a specific type. The right answer is described in Catch 'em all! here, and it is to use the concrete SomeException
type. The resulting code is:
logExceptions :: MonadUnliftIO m => Logger -> m a -> m a
logExceptions logger program = withException program logEx
where
logEx :: MonadUnliftIO m => SomeException -> m ()
logEx ex = do
logIt Warn logger ["Logging exception: ", (show ex)]
flush logger
Upvotes: 1