関数型プログラミング
計算を、値から値への変換として表す。
中心的な考え方#
| 概念 | 内容 |
|---|---|
| 純粋関数 | 同じ入力に同じ出力。副作用が無い |
| 不変性 | 値を変更せず、新しい値を作る |
| 第一級関数 | 関数を値として扱う |
| 高階関数 | 関数を引数・戻り値にする |
| 参照透過性 | 式をその値で置き換えられる |
なぜ副作用を避けるのか#
純粋関数 f(x) は、x だけを見れば結果が分かる
→ 単体で理解できる
→ テストが書ける(準備が要らない)
→ 並行実行しても安全
→ キャッシュできる
→ 実行順序を入れ替えられる局所的に理解できることが最大の価値。 副作用があると、その関数を理解するのに プログラム全体の状態を知る必要が出てくる。
実務での使い方#
純粋関数型言語(Haskell)を使わなくても、 方針として取り入れられる。
swift
// 手続き的
var result: [Int] = []
for x in xs { if x > 0 { result.append(x * 2) } }
// 関数的
let result = xs.filter { $0 > 0 }.map { $0 * 2 }map、filter、reduce は
ベクトル化とも通じる
「ループを書かずに変換を宣言する」形。
副作用を端に追いやる#
副作用(I/O、DB、乱数、時刻)は無くせない。 中心を純粋に保ち、副作用を外側に集める設計にする。
[外側] 入力を読む
↓
[中心] 純粋な変換(テストしやすい)
↓
[外側] 結果を書くこれはクリーンアーキテクチャの 依存の向きとも一致する。
オブジェクト指向との関係#
対立しない。
- OOP … 状態と振る舞いをまとめ、境界を作る
- FP … 状態の変更そのものを減らす
Swift、Kotlin、Rust、TypeScript はいずれも 両方の要素を持つ。
参考文献#
- John Hughes. Why Functional Programming Matters. The Computer Journal 32(2), 1989. https://doi.org/10.1093/comjnl/32.2.98
- Michael Feathers 他による「Functional Core, Imperative Shell」の議論(Gary Bernhardt, Boundaries, 2012) https://www.destroyallsoftware.com/talks/boundaries
- Robert C. Martin. Clean Architecture: A Craftsman’s Guide to Software Structure and Design. Prentice Hall, 2017.