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
)いくつかのことを知る必要があります。
トライアルを書き留めて、あなたが私を助けてくれることを願っています:
試用
私のような例があります:
x = 5 `div` 0
test = try (print x) :: IO (Either SomeException ())
2つの質問があります。
カスタムエラー出力を設定するにはどうすればよいですか?
すべてのエラーをSomeExceptionに設定するにはどうすればよいですか。:: IO (Either SomeException())
キャッチ/試行
カスタムエラー出力の短い例を教えてください。
Control.Exceptionドキュメントからの推奨事項は次のとおりです。
finally
、bracket
、またはonException
を使用します。try
ファミリーのいずれかを使用するのが最善の選択です。catch
またはcatchJust
を使用します。try
はIO
アクションを実行してEither
を返します。計算が成功した場合、結果はRight
コンストラクターにラップされて提供されます。 (間違っているのではなく、正しく考えてください)。アクションが指定されたタイプの例外をスローした場合、Left
コンストラクターで返されます。例外が適切なタイプのnotであった場合、スタックを伝播し続けます。タイプとして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
はtry
に似ています。最初に、指定されたIO
アクションを実行しようとしますが、例外がスローされると、ハンドラーに例外が与えられ、代替の回答が得られます。
_main = catch (print $ 5 `div` 0) handler
where
handler :: SomeException -> IO ()
handler ex = putStrLn $ "Caught exception: " ++ show ex
_
ただし、1つの重要な違いがあります。 catch
を使用する場合、非同期例外(つまり、throwTo
を介して別のスレッドからスローされる)によってハンドラーを中断することはできません。非同期例外を発生させようとすると、ハンドラーの実行が完了するまでブロックされます。
Preludeには別のcatch
があるため、import Prelude hiding (catch)
を実行することもできます。
handle
は単純にcatch
であり、引数の順序は逆です。どちらを使用するかは、コードを読みやすくするもの、または部分的なアプリケーションを使用する場合により適したものに依存します。それ以外は同じです。
try
、catch
、およびhandle
は、指定/推論された型のall例外をキャッチすることに注意してください。 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
_のようなモナドを使用すると、このタイプのエラー処理が簡単になります。
こちらもご覧ください:
Edward Z. Yangには、haskellの例外処理に関する記事があります: Haskellでエラーを報告する8つの方法の再検討 。
Re:質問3:キャッチとハンドルは same ( hoogle で発見)です。どちらを使用するかの選択は、通常、各引数の長さに依存します。アクションが短い場合は、catchを使用し、逆も同様です。ドキュメントからの単純なハンドルの例:
do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ...
また、ハンドル関数をカリー化してカスタムハンドラーを作成することもできます。 (ドキュメントから改編):
let handler = handle (\NonTermination -> exitWith (ExitFailure 1))
カスタムエラーメッセージ:
do
let result = 5 `div` 0
let handler = (\_ -> print "Error") :: IOException -> IO ()
catch (print result) handler
私もあなたを悩ませる1つのこと(2番目の質問)が:: IO (Either SomeException ())
の記述であることがわかり、それも私を悩ませます。
これからいくつかのコードを変更しました:
let x = 5 `div` 0
result <- try (print x) :: IO (Either SomeException ())
case result of
Left _ -> putStrLn "Error"
Right () -> putStrLn "OK"
これに:
let x = 5 `div` 0
result <- try (print x)
case result of
Left (_ :: SomeException) -> putStrLn "Error"
Right () -> putStrLn "OK"
これを行うには、ScopedTypeVariables
GHC拡張機能を使用する必要がありますが、審美的には価値があると思います。