関数型プログラミング

副作用を避け、純粋関数と不変データを中心にプログラムを構築するパラダイム

プログラミング基礎
関数型プログラミング」の技術書を見る →

関数型プログラミングとは

関数型プログラミング (FP) は、副作用を避け、純粋関数と不変データを中心にプログラムを構築するパラダイムである。Haskell が純粋関数型言語の代表。TypeScriptRustJava も FP の要素を取り入れている。

核となる概念

核となる概念を以下にまとめる。

概念説明
純粋関数同じ入力に対して常に同じ出力、副作用なし
不変性データを変更せず、新しいデータを作る
高階関数関数を引数に取る、または関数を返す
関数合成小さな関数を組み合わせて大きな処理を作る

命令型 vs 関数型

命令型と関数型のコード例を比較する。

// 命令型: 状態を変更
let total = 0;
for (const item of items) {
  if (item.active) total += item.price;
}

同じ集計を関数型で書くと次のようになる。

// 関数型: データを変換
const total = items
  .filter(item => item.active)
  .reduce((sum, item) => sum + item.price, 0);

違いは行数ではなく、total が書き換わる時間帯があるかどうかである。命令型のループでは集計途中の total を誰でも読めるため、条件を 1 つ足したときに加算漏れや二重加算が入り込む余地が残る。関数型の書き方は「絞り込む」と「畳み込む」に分かれているので、条件の追加は filter の差し替えで済み、途中状態が外に漏れない。

不変性

不変性のコード例を示す。

// ❌ ミュータブル
const user = { name: 'Alice', age: 30 };
user.age = 31; // 元のオブジェクトを変更

// ✅ イミュータブル
const updatedUser = { ...user, age: 31 }; // 新しいオブジェクトを作成

高階関数

高階関数のコード例を示す。

// map, filter, reduce は高階関数
const names = users.map(u => u.name);
const adults = users.filter(u => u.age >= 18);

// 関数を返す関数
const multiply = (factor: number) => (x: number) => x * factor;
const double = multiply(2);
double(5); // 10

パイプライン

パイプラインのコード例を示す。

// 関数を合成してパイプラインを作る
const processOrder = (order: Order) =>
  pipe(
    validateOrder,
    calculateTotal,
    applyDiscount,
    generateInvoice,
  )(order);

pipe は標準の JavaScript には無く、Ramda の R.pipe などライブラリが提供する関数である。左から右へ適用されるので読む順序と処理順序が一致し、右から左へ適用される compose とは向きが逆になる。各段が前段の戻り値だけを受け取る形にそろえないと合成できないため、途中で引数を 1 つ増やしたい要求が出た時点でパイプラインは崩れる。そこが手続き的に書き下す場合との実務上の分かれ目になる。

Lambda と FP

AWS Lambda のハンドラーは event を受け取って値を返す形なので、関数型と相性が良いと説明されることが多い。ただし実務のハンドラーはデータベースへの書き込みや外部 API 呼び出しといった副作用そのものを目的にしており、それ自体は純粋関数ではない。効くのは分離のほうで、入力の検証・判定・変換だけを純粋関数として切り出し、書き込みは薄い層に閉じ込める。純粋部分は event 相当のオブジェクトを組み立てるだけで単体テストでき、モックが要る範囲も薄い層だけに縮む。

冪等性は純粋性から自動的には得られない別の性質である。SQS 標準キューのような at-least-once 配信では同じ event が複数回届くため、event から冪等キー (注文 ID など) を取り出し、DynamoDB などの永続層で処理済みかを確認してから副作用を実行する仕組みが必要になる。Powertools for AWS Lambda の idempotency 機能はこの型を実装しており、処理中・完了・期限切れといった状態を永続層に記録して二重実行を防ぐ。純粋関数として書いたから冪等、とは言えない。

FP のメリット

純粋関数は入出力だけでテストでき、共有状態がないためデータ競合が起きず並行処理に強い。副作用がないので挙動が予測可能で、小さな関数を組み合わせて複雑な処理を構築できる合成のしやすさも利点だ。

実務での活用方法は関連書籍にも詳しい。

この記事は役に立ちましたか?

関連用語

関連する記事