関数型言語

関数を第一級の値として扱うプログラミングパラダイム。理論的基盤は λ計算。

プログラマ向けの一言

関数を変数に入れたり、引数に渡したり、返り値にできる言語。 JavaScript/TypeScript を書いてる時点で、あなたは既に関数型プログラミングを部分的にやっている。

コードで理解する

// 関数が「第一級 (first-class)」= 値として扱える
const double = (x: number) => x * 2;
const apply = (f: (x: number) => number, x: number) => f(x);
apply(double, 5); // 10

// 高階関数: 関数を受け取る or 返す関数
const numbers = [1, 2, 3, 4, 5];
numbers.filter(x => x % 2 === 0);  // [2, 4]   ← filter は関数を受け取る
numbers.map(x => x * 2);           // [2, 4, 6, 8, 10]
numbers.reduce((sum, x) => sum + x, 0); // 15

// 関数の合成
const compose = <A, B, C>(f: (b: B) => C, g: (a: A) => B) => (a: A) => f(g(a));
const increment = (x: number) => x + 1;
const doubleAndIncrement = compose(increment, double);
doubleAndIncrement(3); // 7

命令的 vs 宣言的

FP は宣言的パラダイムの一種。両者は「計算をどうモデル化するか」の根本が違う。

命令的(imperative)宣言的・関数型(declarative / functional)
計算とは状態を逐次的に書き換える手続き式を値に評価すること
中心概念文(statement)・代入・ループ式(expression)・関数適用・再帰
状態可変(mutable)。時間で変わる不変(immutable)。新しい値を作る
制御for / while / goto高階関数(map/fold)・再帰・パターンマッチ
思考の単位「どう(how)変えるか」「何(what)であるか」
副作用コードに散在型で隔離(Monad / Effect)
理論基盤チューリング機械(テープ=状態)λ計算(書き換え=簡約)

両者は計算能力としては等価(チューリング完全)。違うのは何が一級市民か——命令型は「文と状態」、関数型は「式と値」。

FP の核となる5概念

概念意味コードの感触
第一級関数関数を値として変数・引数・返り値に使えるconst f = x => x*2
高階関数 (HOF)関数を受け取る/返す関数map, filter, compose
関数合成小さい関数を繋いで大きい関数を作るcompose(g, f)(x) = g(f(x))
不変性値を破壊的変更せず新しい値を作る[...arr, x](push しない)
参照透過性式をその値で置換しても意味が変わらないf(2) を常に 4 に置換可

参照透過性が FP の意味論的な要。これが成り立つと、等式推論(コードを数式のように書き換えて推論)が可能になり、メモ化・stream fusion のような最適化や、並列化(順序非依存)が安全に行える。副作用はこの性質を壊すので、FP では型で隔離する。

関数型の中心的な考え方

// 1. イミュータビリティ(変更しない)
const bad = (arr: number[]) => { arr.push(1); return arr; }; // 元の配列を壊す
const good = (arr: number[]) => [...arr, 1];                 // 新しい配列を返す

// 2. 副作用の分離
// 副作用あり: DB読み書き、console.log、ファイルI/O、乱数
// 純粋: 同じ入力に対して常に同じ出力。副作用なし。
const pure = (x: number) => x * 2;          // 何回呼んでも同じ
const impure = () => Math.random();          // 毎回違う

// 3. 宣言的(何をするか)vs 命令的(どうやるか)
// 命令的
let sum = 0;
for (let i = 0; i < numbers.length; i++) sum += numbers[i];
// 宣言的
const total = numbers.reduce((s, x) => s + x, 0);

代表的な言語

押さえどころ(カード化候補)

関連

Graph