関数適用スタイル
コードを書くとき、暗黙的に置く「主語」の位置。データを起点に考えるか、操作を起点に考えるかというパラダイムの違いを反映する。
shape.area() // OOP的:「shapeが」areaを返す(データ起点)
area(shape) // FP的:「area関数が」shapeを処理する(操作起点)
どちらも同じ計算だが、読み方が異なる。これは単なる構文の好みではなく、データと操作の関係性をどう捉えるかというパラダイムの違い。
2つの思考スタイル
データ起点の思考
- 「このデータに対して何ができるか?」という問いから出発
- IDE で
shape.と打つと操作一覧が出る - OOP 的、Kotlin の拡張関数、TypeScript の
pipe() - メソッドチェーン:
shape.area().format()
操作起点の思考
- 「この操作は何に対して使えるか?」という問いから出発
- 関数の型シグネチャ
Shape -> numberを見る - FP 的、Haskell の関数合成
- 入れ子:
format(area(shape))
パラダイムとの対応
| 観点 | OOP(データ起点) | FP(操作起点) |
|---|---|---|
| 知識の所在 | データが振る舞いを持つ | 関数がデータを知る |
| 拡張の方向 | 新しい種類のデータを追加しやすい | 新しい操作を追加しやすい |
| 凝集の単位 | クラス(データ+操作) | モジュール(型+関数群) |
この拡張方向の違いはExpression Problemとして知られる。
両立するアプローチ
- パイプ演算子(Elixir, F# の
|>):関数型言語でもデータ起点に書ける - 拡張関数(Kotlin):OOP 的な構文でありながら FP 的に操作を追加できる
pipe()関数(TypeScript/fp-ts):パイプ演算子の代替
どちらを選ぶか
「どちらが正しい」ではなく、問題領域と個人の思考スタイルによって使い分けるもの。
- 変換が1回きり →
pipeでデータ起点に書く - 変換を再利用する →
flowで関数を合成する - シンプルな場合 → 素朴な入れ子で十分
どの構文を選んでも、下にある設計思想(凝集度、結合度、Expression Problemへの対処)は共通している。
関連
- パイプ演算子 - 関数型でデータ起点の記述を可能にする構文
- 拡張関数 - OOP と FP の中間解
- 関数合成 - 操作起点の思考における関数の組み立て
- Expression Problem - 拡張方向のトレードオフ
- 関数型プログラミング - 操作起点思考のパラダイム
- 主語の位置:OOPと関数型における関数適用スタイルの違い - 詳細な考察