1. なぜ「例外」を避けるべきなのか?
プログラミングの世界で、try-catch文による例外処理に疲弊したことはありませんか?従来の例外処理は、プログラムのメインの流れを中断させ、コードを複雑にする「飛び道具」のような存在です。
関数型プログラミングでは、エラーを「異常な出来事」ではなく、「データの一形態」として扱います。エラーを型として表現し、それをデータ変換パイプラインに乗せることで、例外処理を特別扱いせず、メインロジックと同じ視点で綺麗に書くことができるようになります。
2. 基礎知識:エラーを「型」として捉える
関数型プログラミングにおけるエラー処理の基本は、Either型という概念です。
Either型は、左側(Left)にエラー値、右側(Right)に成功値を保持する入れ物です。
・Right: 処理が成功した場合(正しい結果)
・Left: 処理が失敗した場合(エラー情報)
プログラムを「成功ルート」と「エラールート」という2つの並行するレーンとして捉え、データがそのどちらを通っているかを管理します。
3. 実装:データ変換としてのエラー処理
エラー処理を「制御構造」ではなく「データ変換」と見なすと、処理の流れは非常にシンプルになります。
例えば、文字列を数値に変換し、それを2倍にするという処理を考えます。
1. 文字列が数値でない場合 → エラー(Left)を返す。
2. 数値である場合 → 数値を2倍にする(Right)処理を行う。
ここで重要なのが map や mapLeft といった高階関数です。これらを使うことで、成功した時だけ値を変換したり、逆にエラー時だけエラーメッセージを加工したりと、パイプラインの中で柔軟にデータを操作できます。
4. サンプルプログラム
JavaScript(TypeScript風の擬似コード)で、Either的な考え方を実装した例です。
// エラーをLeft、成功をRightとして扱うための関数
const right = (val) => ({ type: ‘Right’, value: val });
const left = (err) => ({ type: ‘Left’, value: err });
// 数値に変換する関数(失敗する可能性がある)
const parseNumber = (str) => {
const num = parseInt(str);
return isNaN(num) ? left(“数値ではありません”) : right(num);
};
// 数値を2倍にする関数
const double = (n) => n 2;
// パイプライン処理
const processInput = (input) => {
const result = parseNumber(input);
// 成功時(Right)のみ double を適用する
// エラー時(Left)は何もせずそのまま通過する
if (result.type === ‘Right’) {
return right(double(result.value));
}
return result;
};
// 実行確認
console.log(processInput(“10”)); // 結果: { type: ‘Right’, value: 20 }
console.log(processInput(“abc”)); // 結果: { type: ‘Left’, value: “数値ではありません” }
5. 応用・注意点
この手法の最大の利点は、「関数の合成」が容易になることです。例外を投げないため、関数同士をパイプラインのように繋げても、どこかでエラーが発生すれば、そのエラー値が最後まで安全に運ばれるだけになります。
注意点としては、全ての関数を「Eitherを返す形」に統一する必要があるため、既存のライブラリや古いコードと組み合わせる際には、関数の戻り値をラップする「アダプター」を挟む工夫が必要です。
「例外」という言葉のニュアンスを捨て、「計算の別ルート」という考え方を導入するだけで、あなたの書くコードは驚くほど堅牢で読みやすくなるはずです。ぜひ今日から試してみてください。

コメント