16

良くも悪くも、Haskell の人気のあるServantライブラリにより、 を含むモナド変換子スタックでコードを実行することが一般的になりましたExceptT err IO。サーバント自身のハンドラーモナドはExceptT ServantErr IO. 多くの人が主張するように、展開に失敗するには複数の方法があるため、これは動作するのがやや面倒なモナドです: 1) ベースからの通常の例外を介して、または 2) を返す。IOLeft

Ed Kmett のexceptionsライブラリが役立つように次のように説明しています。

継続ベースのモナド、およびErrorT e IO複数の失敗モードを提供するようなスタックは、この [ MonadMask] クラスの無効なインスタンスです。

これは、リソース管理を行うための便利な [のポリモーフィック バージョン] 関数にMonadMaskアクセスできるため、非常に不便です (例外などによるリソースのリークはありません)。bracketしかし、サーバントのHandlerモナドでは使えません。

私はあまり詳しくありませんが、解決策はmonad-control多くのパートナーライブラリを使用することでありlifted-baselifted-asyncモナドに次のようなリソース管理ツールへのアクセスを許可することであると言う人もいますbracket(おそらくこれはExceptT err IO友人にも機能しますか?)。

しかし、それはコミュニティで支持を失ってmonad-controlいるようですが、代替案が何であるかはわかりません. Snoyman の最近のライブラリでさえ、Kmett のライブラリを使用し、.safe-exceptionsexceptionsmonad-control

Haskell を真剣に使用しようとしている私のような人々のために、誰かが現在の話を明確にすることができますか?

4

2 に答える 2

7

で作業し、最後IOに type の値を返し、IO (Either ServantErr r)それをラップしExceptTてハンドラーの型に合わせることができます。これにより、bracketで通常どおり使用できますIO。このアプローチの問題点の 1 つは、ExceptT提供される「自動エラー管理」が失われることです。つまり、ハンドラーの途中で失敗した場合は、明示的なパターン マッチを実行する必要がありますEither


上記は基本的に のMonadTransControlインスタンスを再実装してExceptTいます。

instance MonadTransControl (ExceptT e) where
    type StT (ExceptT e) a = Either e a
    liftWith f = ExceptT $ liftM return $ f $ runExceptT
    restoreT = ExceptT

monad-controlは、のような関数を持ち上げる場合には問題なく動作bracketしますが、次のような関数には奇妙なコーナー ケースがあります (このブログ投稿から取得)。

import Control.Monad.Trans.Control

callTwice :: IO a -> IO a
callTwice action = action >> action

callTwice' :: ExceptT () IO () -> ExceptT () IO ()
callTwice' = liftBaseOp_ callTwice

callTwice'何かを出力し、直後に失敗するアクションに渡した場合

main :: IO ()
main = do
    let printAndFail = lift (putStrLn "foo") >> throwE ()
    runExceptT (callTwice' printAndFail) >>= print  

アクションの最初の実行が失敗した後に停止する必要があると私たちの直感が言っている場合でも、とにかく「foo」を2回出力します。


別のアプローチは、resourcetライブラリを使用して ExceptT ServantErr (ResourceT IO) rモナドで作業することです。の代わりに のresourcetような関数を使用し、最後に次のようにモナドを適応させる必要があります。allocatebracket

import Control.Monad.Trans.Resource
import Control.Monad.Trans.Except

adapt :: ExceptT ServantErr (ResourceT IO) r -> ExceptT err IO r 
adapt = ExceptT . runResourceT . runExceptT

または好き:

import Control.Monad.Morph

adapt' :: ExceptT err (ResourceT IO) r -> ExceptT err IO r 
adapt' = hoist runResourceT
于 2016-11-02T07:30:19.707 に答える
3

私の推奨事項: コードを ExceptT ではなく IO に置き、各ハンドラー関数をExceptT . try.

于 2016-11-10T03:32:44.227 に答える