1. ホーム
  2. haskell

[解決済み] Haskellの例外処理

2023-06-24 10:49:39

質問

3つのHaskell関数の使い方を理解する手助けが欲しいです。

  • を試す ( Control.Exception.try :: Exception e => IO a -> IO (Either e a) )
  • キャッチ ( Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a )
  • ハンドル ( Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a )

いくつか知りたいことがあります。

  1. どの機能をいつ使えばいいのか?
  2. この関数はどのように使うのか、簡単な例を挙げて説明します。
  3. catchとhandleの違いは何ですか?順番が違うだけで、ほぼ同じシグネチャを持っています。

私の試行錯誤を書き留めますので、よろしくお願いします。

試す

みたいな例があります。

x = 5 `div` 0
test = try (print x) :: IO (Either SomeException ())

2つ質問があります。

  1. カスタムエラー出力を設定するにはどうすればよいですか?

  2. すべてのエラーをSomeExceptionに設定するために、私は何をすることができ、私はそれを書く必要がありません。 :: IO (Either SomeException())

catch/try

カスタムエラー出力の短い例を示してもらえますか?

どのように解決するのですか?

どのような場合に、どの機能を使用するのですか?

Control.Exceptionのドキュメントにある推奨事項は以下の通りです。

  • 例外が発生した際に、何らかの後始末をしたい場合は finally , bracket または onException .
  • 例外の後に回復して他のことをするために、最適な選択は try ファミリーを使用することです。
  • ... 非同期例外から回復する場合を除き、その場合は catch または catchJust .

try :: Exception e => IO a -> IO (どちらかのe a)

try を取る。 IO アクションを実行し Either . 計算が成功した場合、その結果は Right コンストラクタに包まれて与えられる。(間違ったことではなく、正しいことと考えてください)。もしアクションが例外を投げたら が指定されたタイプの で返されます。 Left コンストラクタで返されます。もし例外が ではなく でない場合、例外はスタック上に伝搬し続けます。指定する SomeException を型として指定すると、すべての例外をキャッチすることになります。

純粋な計算からの例外を捕捉したい場合は、その例外を捕捉するために evaluate の中で強制的に評価させる必要があります。 try .

main = do
    result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int)
    case result of
        Left ex  -> putStrLn $ "Caught exception: " ++ show ex
        Right val -> putStrLn $ "The answer was: " ++ show val

catch :: Exception e => IO a -> (e -> IO a) -> IO a

catchtry . これはまず、指定された IO アクションを実行しようとしますが、もし例外が発生した場合は、代替の答えを得るためにハンドラに例外が渡されます。

main = catch (print $ 5 `div` 0) handler
  where
    handler :: SomeException -> IO ()
    handler ex = putStrLn $ "Caught exception: " ++ show ex

しかし には1つ重要な違いがあります。を使用する場合 catch を使う場合、ハンドラは非同期例外 (すなわち、他のスレッドから throwTo ). 非同期例外を発生させようとすると、ハンドラの実行が終了するまでブロックされます。

別の catch があるので、前奏曲で import Prelude hiding (catch) .

handle :: 例外e => (e -> IO a) -> IO a -> IO a

handle は、単純に catch であり、引数の順番が逆になっています。どちらを使うかは、コードを読みやすくするか、部分適用を使いたい場合にどちらが合うかによります。それ以外は同じです。

tryJust、catchJust、handleJust

以下のことに注意してください。 try , catchhandle がキャッチするのは すべて の例外を捕捉します。 tryJust とその仲間では、特に処理したい例外をフィルタリングするセレクタ関数を指定することができます。例えば、全ての算術演算エラーは ArithException . をキャッチしたいだけなら DivideByZero をキャッチしたいだけなら、できます。

main = do
    result <- tryJust selectDivByZero (evaluate $ 5 `div` 0)
    case result of
        Left what -> putStrLn $ "Division by " ++ what
        Right val -> putStrLn $ "The answer was: " ++ show val
  where
    selectDivByZero :: ArithException -> Maybe String
    selectDivByZero DivideByZero = Just "zero"
    selectDivByZero _ = Nothing


純度に関する注意事項

この種の例外処理は、不純物のないコード (すなわち IO モナド) でのみ発生することに注意してください。純粋なコードでエラーを処理する必要がある場合は、以下のような方法で値を返すことを検討する必要があります。 Maybe または Either のような代数的なデータ型に置き換えてください。この方がより明示的で、どこで何が起こりうるかが常にわかるので、しばしば好ましい。のようなモナドは Control.Monad.Error のようなモナドは、この種のエラー処理をより簡単にします。


こちらもご覧ください。