短縮版
hereと同じ質問ですMonadResource
が、明示的ではなく一般的なインスタンス内にありますResourceT m
。
ロングバージョン
catch
次のような関数をどのように定義しますか。
import Control.Exception (Exception, IOException)
import Control.Monad.Trans.Resource (MonadResource, runResourceT)
catch :: (MonadResource m, Exception e) -> m () -> (e -> m ()) -> m ()
catch = undefined
-- 'a' and 'b' are functions from an external library,
-- so I can't actually change their implementation
a, b :: MonadResource m => m ()
a = -- Something that might throw IO exceptions
b = -- Something that might throw IO exceptions
main :: IO ()
main = runResourceT $ do
a `catch` \(e :: IOException) -> -- Exception handling
b `catch` \(e :: IOException) -> -- Exception handling
私が遭遇する問題は次のとおりです。
- では
Control.Exception
、catch
裸IO
の s でのみ機能します。 - では
Control.Exception.Lifted
、catch
のインスタンスが必要ですが、残念ながらそうMonadBaseControl
ではありません(なぜだろうか)。MonadResource
MonadResource
MonadThrow
'catch' に相当するものなしで関数を定義することを意味しますmonadThrow
(そして、なぜだろうか)。
IO
例外を処理する唯一の方法はレイヤーを終了することのように見えますが、ResourceT
これは私を悩ませています: モナドトランスフォーマースタックを経由せずにローカルで例外を処理できるようにしたい.
参考までに、私の実際のコードでは、a
とb
は実際には のhttp
関数ですNetwork.HTTP.Conduit
。
あなたの洞察に感謝します。
問題のある最小限のコード
ghc --make example.hs
ライブラリがインストールされた状態でコンパイル可能http-conduit
:
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
import Control.Exception.Lifted (IOException, catch)
import Control.Monad.Base (liftBase)
import Control.Monad.Error (MonadError(..), runErrorT)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Trans.Resource (MonadResource, runResourceT)
import Data.Conduit
import Data.Conduit.List (consume)
import Data.Conduit.Text (decode, utf8)
import Data.Text (Text)
import Network.HTTP.Client
import Network.HTTP.Conduit (http)
main :: IO ()
main = do
result <- runErrorT $ runResourceT f
putStrLn $ "OK: " ++ show result
f :: (MonadBaseControl IO m, MonadResource m, MonadError String m) => m [Text]
f = do
req <- liftBase $ parseUrl "http://uri-that-does-not-exist.abc"
manager <- liftBase $ newManager defaultManagerSettings
response <- (http req manager `catch` \(e :: IOException) -> throwError $ show e)
response $$+- decode utf8 =$ consume
実行すると、このプログラムはエラーで終了し、次の出力が表示されます。
InternalIOException getAddrInfo: does not exist (Name or service not known)