読書メモ:JavaScript 関数型プログラミング

読書メモ:JavaScript 関数型プログラミング

July 15, 2023

上記の本を再読。今日では関数型プログラミングのエッセンスが広く浸透しているように思う。たとえ開発者が意識していなくとも、関数型プログラミング由来の考え方を自然なうちに用いている場面が多いはず。読書記録として、本書冒頭の部分を抜粋してまとめる。


関数型プログラミングとは何か #

オブジェクト指向は可動部をカプセル化することでコードを理解しやすくする。関数型プログラミングは可動部を極力減らすことでコードを理解しやすくする。(OO makes code understandable by encapsulating moving parts. FP makes code understandable by minimizing moving parts.)

関数型プログラミングとは、「副作用を避け」、アプリケーションにおける「状態遷移を減らす」ために、データに「関する制御フローと処理を抽象化する」ことを目的として関数を記述していくプログラミングパラダイム。

関数型プログラミングは、純粋関数を使いプログラムを構築する。次の特徴を持つものを純粋関数と呼ぶ。

  • 提供される入力値にのみ依存する。関数の実行中や関数呼び出しが行われる間に状態が変更する可能性がある、隠された値や外部スコープの値には一切依存しない。同じ入力に対して、常に同じ結果を返す。(参照透過性がある)
  • 関数自身のスコープの外にある値を一切変更しない。つまり、グローバルオブジェクトや参照渡しされた引数を変更しない。(副作用がない)

これらの要件を満たさない関数は不純である。

/**
 * 不純な関数
 */
var counter = 0;
function increment() {
  return ++counter;
}

/**
 * 純粋な関数
 */
var increment = (counter) => counter + 1;

関数が外部のリソースを読み書きする場合、その関数は副作用を持つ。たとえば Date.now() も副作用を持つ関数といえる。なぜならば、時刻という外部の変化する要因に依存しており、出力結果は一定ではないためである。

ただし、副作用をまったく発生させないプログラムとは、ユーザの入力を受け取ったり、コンソールへの出力もできないことになる。そのようなプログラムは実用できない。そのため実際には、純粋なコードと不純なコードを分離する方針がとられる。

そして関数型プログラミングは、分解(プログラムを小さい処理単位に分ける)と合成(小さい処理単位を結合する)の相互作用から成る。特定のタスクを論理的なサブタスク(関数)に分解し、それらがよりシンプルで純粋性を持った処理単位になるまで分解する。

この結果、コードのテストや保守が簡単になるのが関数型プログラミングである。

関数合成用の関数の例 #

https://gist.github.com/luijar/ce6b96f13e31cb153093

function compose(...fns) {
  return function (x) {
    return fns.reduce((acc, fn) => fn(acc), x);
  };
}

const calculate = compose(
  (x) => x + 3,
  (x) => x * 4,
  (x) => x / 5
);

const result = calculate(2); // 4 = (((2 + 3) * 4) / 5)