exceptionlisted
Install: claude install-skill ncaq/konoka
# 例外処理の原則
## `error`関数の禁止
純粋関数空間の中で例外を投げられる`error`関数の使用は原則として禁止です。
[haskell-tasuke:partial-function](../partial-function/SKILL.md)
でも触れましたが、
純粋関数空間で例外を投げてしまうと、
呼び出し側で捕捉するのが困難だからです。
また例外に入っているのが`String`という非構造化されたデータであると、
デバッグが難しくなります。
`throw`(導入されている場合は非純粋であることを明示した`impureThrow`)の方が、
例外に型がついているという点ではまだマシですが、
こちらも純粋関数空間で例外を投げているのは同じなのでやはり推奨されません。
呼び出し側に`MonadThrow`や`Either`などを使ってエラー情報を伝播させてください。
### 例外
論理的にどう考えても発生しないはずの場所なら許容されます。
そんなおかしなことが起きているなら、
アプリケーション自体を終了させるのが適切なレベルの話です。
それでもなるべく型を作って例外を投げるのが望ましいです。
## `undefined`はPRを出すときにはクリーンアップしましょう
Haskell標準で用意されている`undefined`は、
開発中に仮の実装として使うのは便利です。
Rustの`todo!`と同じように使えます。
例えばテストファーストで開発をする時に、
とりあえずコンパイルを通すだけの関数シンボルを`undefined`で埋めて、
テストコードを書いてから実装を進めるといった使い方ができます。
しかし`undefined`は結局は純粋空間でも例外を投げる関数なので、
PRを出すときには`undefined`をクリーンアップしてください。
残っていると警告が出るはずなので見落としはあまりないとは思いますが。
## 例外は型をつけよう
`throwString`のような関数を使うより、
ちゃんと例外に型をつけて`throwM`などで型がついた例外を使いましょう。
例外の状況を伝えるデータ型は、
`Text`などの文字列型を使うのではなく、
なるべく構造的なデータ型をフィールドとして持ってください。
ただしcycle importが発生する場合は、
呼び出し側で`Text`に変換するのもやむを得ないでしょう。
## エラーを握り潰すのは禁止
`IO`の文脈などで例外が生じた場合に、
単に握りつぶして何もしない行為は禁止です。
`IO`は文脈的に既に例外が発生する可能性があることを示しているので、
例外が上位に伝播する���とは許容されています。
なので握りつぶすぐらいなら、
例外をそのまま上位に任せてしまう方が概ね適切です。
例外が生じた時に、
そこで例外を処理するのが完全に適切なら、
問題の程度に応じたログを出して処理してください。
単にデバッグを楽にするだけで解決は出来ない場合は例外を再送出してください。
例外だけではなく`Either`の`Left`なども適切に処理してください。
`Left`に対してデフォルト値やフォールバック値を使ってください。
想定外で対処不能なら`MonadThrow`の文脈に載せて上位に任せてください。
## `IO`が既にコンテキストにある状態で`Maybe`や`Either`で包むのは微妙
`IO`は例外が発生する可能性がある文脈を十分に表現してい