Как записать все исключения в Haskell?
Оригинальное название: Как обращаться с несколькими экземплярами типов исключений при проверке всех исключений?
У меня есть следующий импорт (обратите внимание, что моя прелюдия на самом деле ClassyPrelude, которая использует UnliftIO.Exception). Обратите внимание, что System.Logger из tinylog, тонкой библиотеки поверх fast-logger.
import Prelude hiding(log)
import System.Logger hiding(log)
import qualified System.Logger as TL
И следующая функция:
logExceptions :: MonadUnliftIO m => Logger -> m a -> m a
logExceptions logger program = withException program
(\ex -> do
logIt Warn logger ["Logging exception: ", (show ex)]
flush logger
)
Помещение лямбды в локальную функцию с типом может сделать это немного более понятным:
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
Это приводит к следующей ошибке компиляции:
* 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
|
Наиболее тревожным моментом является plus 30 instances involving out-of-scope types
, Я мог бы скрыть этот импорт, чтобы немного улучшить ситуацию:
import GHC.Exception.Type hiding(SomeException)
import GHC.IO.Exception hiding(IOException, SomeAsyncException)
Но вряд ли разумно пройтись и найти все 30+ типов исключений и замаскировать их все таким образом. Я предполагаю, что я делаю что-то совершенно не так, или мне действительно нужно пройти через все это и замаскировать?
Примечание:
- мой
logIt
Функция это просто тонкая обертка вокругlog
функция из tinylog - не стесняйтесь заменить все, что чувствует себя эргономичным.
1 ответ
Теперь я понимаю, что моя проблема заключалась в том, что для Exception
аргумент, так как это полиморфная функция, как указано в моем вопросе, и нет сайта вызова, чтобы сузить это до определенного типа. Правильный ответ описан в Catch 'em all! здесь, и это использовать бетон SomeException
тип. Полученный код:
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