Поймать SomeException с помощью ExceptT

Я пытаюсь использовать монадный преобразователь ExceptT для перехвата любого исключения, выдаваемого функцией, например так:

import Control.Exception
import Control.Monad.Trans.Except

badFunction :: ExceptT SomeException IO ()
badFunction = throw DivideByZero

main :: IO ()
main = do
    r <- runExceptT badFunction
    case r of Left _ -> putStrLn "caught error"
              Right _ -> putStrLn "nope, didn't catch no error" 

... но исключение благополучно пролетает мимо. Что я делаю не так?

Изменить: чтобы уточнить, цель состоит в том, чтобы поймать любое исключение, созданное функцией, независимо от того, как это исключение было создано. Если это имеет какое-то значение, реальный вызов функции находится в нижней части довольно глубокого стека преобразования монад. Я не возражаю против пропуска таких вещей, как брошенные строки (плохой программист!).


person Rob Agar    schedule 15.10.2014    source источник


Ответы (2)


Во-первых, вы поймаете свое исключение во время выполнения. Это можно сделать с помощью monad-controllifted-base) или exceptions. У Майкла Сноймана есть хорошая статья, в которой они сравниваются: Исключения и преобразователи монад

Во-вторых, вы встраиваете пойманное исключение в ExceptT.

Вот полный рабочий код:

import Control.Exception.Lifted
import Control.Monad.Trans.Except

badFunction :: ExceptT SomeException IO ()
badFunction = throw DivideByZero

intercept
  :: ExceptT SomeException IO a
  -> ExceptT SomeException IO a
intercept a = do
  r <- try a
  case r of
    Right x -> return x
    Left e -> throwE e

main :: IO ()
main = do
    r <- runExceptT $ intercept badFunction
    case r of Left _ -> putStrLn "caught error"
              Right _ -> putStrLn "nope, didn't catch no error" 

Более компактное (но, возможно, несколько менее очевидное) определение intercept таково:

intercept
  :: ExceptT SomeException IO a
  -> ExceptT SomeException IO a
intercept = handle throwE
person Roman Cheplyaka    schedule 15.10.2014

Я полагаю, вы хотите throwE, а не throw.

Кроме того, это будет ArithException, а не SomeException.

person Josh Kirklin    schedule 15.10.2014