从 Exception 到 ExceptT 的映射

Mapping from Exception to ExceptT

我正在阅读有关异常的章节,因此我决定尝试将异常映射到 ExceptT monad 转换器。我的函数 getInt 可以从控制台读取一个整数并将异常报告为 Left "what's wrong":

getInt :: ExceptT String IO Int
getInt = ExceptT $ do
    Right . read <$> getLine
    `catch` \(e :: ErrorCall) -> return . Left . show $ e

我试过了,可惜没有捕获到异常,懒惰在嘲笑我。好的,让我严格使用 seq.

getInt :: ExceptT String IO Int
getInt = ExceptT $ do
    read <$> getLine >>= seq <*> return . Right
    `catch` \(e :: ErrorCall) -> return . Left . show $ e

结果是:左"Prelude.read: no parse"。现在可以使用了!

我的问题是 - 这是将异常映射到 ExceptT 的正确方法吗?

正确的做法确实是强制求值read。这可以通过按值调用应用程序 $!

更优雅地完成
getInt :: ExceptT SomeException IO Int
getInt = ExceptT $ liftIO $ try @SomeException $ do
  i <- getLine
  pure $! read i

通常最好将底层 monad 参数化(出于可测试性和可重用性的原因),因此我们可以这样写

getInt :: (MonadError SomeException m, MonadIO m) => m Int
getInt = liftEither =<< liftIO $ try @SomeException $ do
  i <- getLine
  pure $! read i

insdead 一个具体的 monad 转换器堆栈 ExceptT SomeException IO Int