1. 導入:なぜエラーを文字列で管理してはいけないのか?
プログラムを書いていると、つい「エラー内容をとりあえず文字列(String)で返す」という処理をしてしまいがちです。しかし、文字列は「何が起きたか」を人間が読むには便利ですが、プログラムが「どう対処すべきか」を判断するには不向きです。
例えば、「ネットワークエラーならリトライする」「ユーザー入力エラーなら入力を促す」といった処理を文字列の比較で行うと、スペルミスや仕様変更で簡単にバグが生まれます。今回紹介する「エラーを型として定義する(Error messages as Types)」手法を使うことで、プログラムはより堅牢になり、エラー対応の自動化も驚くほど簡単になります。
2. 基礎知識:代数的データ型(ADT)とは
関数型プログラミングには「代数的データ型(Algebraic Data Types)」という仕組みがあります。これは、「A または B または C である」というような、データの種類を厳密に定義する機能です。
エラーを単なるメッセージの入れ物ではなく、「どのような種類のエラーがあり得るか」というカタログとして定義することで、コンパイラが「全てのエラーケースを処理したか?」をチェックしてくれるようになります。これにより、エラー処理の漏れを未然に防ぐことができます。
3. 実装:エラーを構造化する
エラーを型として定義する際は、以下のように「何が起きたか」という情報を構造化します。
例えば、ユーザー情報の処理であれば「ユーザーが見つからない場合」と「データベースが落ちている場合」で、プログラムがとるべき行動は全く異なりますよね。これらを別の型として定義します。
4. サンプルプログラム
以下は、TypeScriptのような型システムを持つ言語や、関数型言語の概念を用いたエラー定義の例です。
/ エラーの種類を明確に定義する /
type AppError =
| { type: ‘USER_NOT_FOUND’, userId: string } // ユーザーが存在しないエラー
| { type: ‘DATABASE_ERROR’, retryable: boolean }; // DBエラー(リトライ可能かどうかのフラグ付き)
/ エラーを処理する関数 /
function handleAppError(error: AppError) {
switch (error.type) {
case ‘USER_NOT_FOUND’:
console.log(`ユーザー ${error.userId} は見つかりませんでした。`);
break;
case ‘DATABASE_ERROR’:
if (error.retryable) {
console.log(“DBエラーです。自動リトライを実行します。”);
} else {
console.log(“致命的なDBエラーです。管理者に連絡してください。”);
}
break;
}
}
/ 実行例 /
const myError: AppError = { type: ‘DATABASE_ERROR’, retryable: true };
handleAppError(myError); // 正しくリトライの判定が行われる
5. 応用・注意点:現場で役立つヒント
この手法の最大の利点は「多言語化(ローカライズ)」が非常に楽になることです。画面に表示するメッセージをコード内にハードコーディングするのではなく、エラーの「型(USER_NOT_FOUNDなど)」をキーにして、翻訳ファイルから文言を引っ張ってくるようにすれば、言語の切り替えも一箇所で済みます。
注意点としては、エラー型を増やしすぎると管理が大変になることです。似たようなエラーは共通化しつつ、プログラムが「判断を変える必要がある境界線」で型を分けるのが、設計のコツです。
エラーを型として扱うことは、単なる規約ではなく「エラー処理の自動化」に向けた第一歩です。ぜひ次回の開発から取り入れてみてください。

コメント