エラーハンドリング
この章では、例外をトラップしたり処理したりするプログラムを構築するためのコンビネーター一式を検討する。
例外について
ある操作が成功するかどうかは、ネットワークの健全性、テーブルの現在の内容、ロックの状態など、予測できない要因に依存します。そのため、EitherT[Executor, Throwable, A]
のような論理和ですべてを計算するか、明示的に捕捉されるまで例外の伝播を許可するかを決めなければならない。つまり、ldbcのアクション(ターゲット・モナドに変換される)が実行されると、例外が発生する可能性がある。
発生しやすい例外は主に3種類ある
- あらゆる種類のI/Oで様々なタイプのIOExceptionが発生する可能性があり、これらの例外は回復できない傾向がある。
- データベース例外は、通常、ベンダー固有のSQLStateで特定のエラーを識別する一般的なSQLExceptionとして、キー違反のような一般的な状況で発生します。エラーコードは伝承として伝えられるか、実験によって発見されなければなりません。XOPENとSQL:2003の標準がありますが、どのベンダーもこれらの仕様に忠実ではないようです。これらのエラーには回復可能なものとそうでないものがある。
- ldbcは、無効な型マッピング、ドライバから返される未知の JDBC 定数、観測される NULL 値、その他 ldbc が想定している不変条件の違反に対して InvariantViolation を発生させます。これらの例外はプログラマのエラーかドライバの不適合を示し、一般に回復不可能です。
モナド・エラーと派生コンバイネーター
すべてのldbcモナドは、MonadError[?[_], Throwable]
を拡張したAsyncインスタンスを提供する。つまり、Executorなどは以下のようなプリミティブな操作を持つことになる
- raiseError: 例外を発生させる (Throwableを
M[A]
に変換する) - handleErrorWith: 例外を処理する (
M[A]
をM[B]
に変換する) - attempt: 例外を捕捉する (
M[A]
をM[Either[Throwable, A]]
に変換する)
つまり、どんなldbcプログラムでもattempt
を加えるだけで例外を捕捉することができるのだ。
val program = Executor.pure[IO, Int](1)
program.attempt
// Executor[IO, Either[Throwable, Int]]
attempt
とraiseError
コンビネータから、Catsのドキュメントで説明されているように、他の多くの操作を派生させることができます。