mokajima.com

関数型プログラミング

はじめに

プログラミングパラダイムとは、プログラミングのスタイルのことを指します。関数型プログラミング(functional programming)はプログラミングパラダイムの1つで、関数の定義と呼び出しの組み合わせによってプログラムを構成することで問題を解決します。

関数型プログラミングはラムダ計算を基礎としています。

ラムダ計算

ラムダ計算は関数抽象と関数適用を形式化した体系です(型無しラムダ計算とモデル - RIMS, Kyoto University - 京都大学より引用)。1930 年代にアメリカのプリンストン大学の数学者 Alonzo Church 氏によって考案されました。

ラムダ計算は関数をブラックボックスとして扱います。関数は入力を受け取り、なんらかの方法で処理を行い、出力を返します。関数は内部の状態を持たない純粋関数です。そのため、同じ入力が与えられれば必ず同じ出力を返します。ラムダ計算は関数抽象と関数適用を繰り返すことで計算を行います。

ラムダ計算はラムダ記法という表記法で関数を表します。以下は f(x)=x+1f(x) = x + 1 をラムダ記法で表した例です。

λx.x+1λx.x + 1

λx.x+1λx.x + 1 は「変数 xx(入力)を受け取り x+1x + 1(出力)を返す関数」を表します。この、関数を定義する構文を関数抽象と言います。また、ここでは λx.x+1λx.x + 1 は名前が付けられていないため、無名関数です。

f(x,y)=x+yf(x, y) = x + y のように、複数の引数を受け取る関数はどうでしょうか。以下は f(x,y)=x+yf(x, y) = x + y をラムダ記法で表した例です。

λx.λy.x+yλx.λy.x + y

λx.λy.x+yλx.λy.x + y は「変数 xx を受け取り『変数 yy を受け取り x+x +y を返す関数』を返す関数」を表します(カリー化、高階関数)。

こうして定義した関数に対し、引数を渡すことを関数適用と言います。例えば λx.x+1λx.x + 1 に 2 を渡すことを「2 に λx.x+1λx.x + 1 を適用する」と言い、以下のように表されます。

(λx.x+1)2(λx.x + 1)2

そして、(λx.x+1)2(λx.x + 1)2 を受けて、実際に xx に 2 を代入し式を簡単にする操作(2 + 1 = 3 を得る)を β 簡約と言います。

(λx.x+1)23(λx.x + 1)2 → 3

関数型プログラミングの特徴

関数型プログラミングは関数を第一級オブジェクトとして扱います。そのため、変数へ関数を代入する、関数の引数として関数を渡す、関数の返り値として関数を返す(高階関数)、といったことが可能です。

関数は副作用のない純粋関数で、同じ入力が与えられれば必ず同じ出力を返します。このような性質を参照透過性と言います。関数型プログラミングでは、参照透過な関数の組み合わせによってプログラムを構成することで問題を解決します。

命令型プログラミングと宣言型プログラミング

プログラミングパラダイムは命令型プログラミング(imperative programming)と宣言型プログラミング(declarative programming)の2つに大別されます。関数型プログラミングは宣言型プログラミングに含まれます。

命令型プログラミングでは、状態を変化させる明示的な命令を連ねてプログラムを構成することで問題を解決します。命令型プログラミングの類推としてレシピが挙げられますが、レシピのように1つ1つの手順を踏むことで出力を得ます。出力を得る方法(how)を記述するプログラミングパラダイムです。

命令型プログラミングに含まれるプログラミングパラダイムには手続型プログラミング(procedural programming)やオブジェクト指向プログラミング(object-oriented programming)などがあります。

以下は 1 から 10 までの和を命令型プログラミングのスタイルで書いた例です。

let total = 0;

for (let i = 1; i <= 10; i++) {
  total += i;
}

console.log(total); // 55

一方、宣言型プログラミングでは、出力がどうあるべきかを宣言してプログラムを構成することで問題を解決します。命令型プログラミングとは対照的に、どんな出力が得られるか(what)を記述するプログラミングパラダイムです。それをどのように実行するかは処理系に委ねられます。

宣言型プログラミングに含まれるプログラミングパラダイムには論理プログラミング(logic programming)や関数型プログラミングなどがあります。

以下は 1 から 10 までの和を関数型プログラミングのスタイルで書いた例です。

const total = Array.from(Array(10))
  .map((_, i) => i + 1)
  .reduce((acc, cur) => acc + cur);

console.log(total); // 55

JavaScript は関数型言語ではありませんが、上記のように関数型プログラミングのスタイルで書くための構文が用意されています。例えば、配列操作のメソッドとして filter() map() reduce() が挙げられます。

参考