みなさま初めまして。
私、2011年4月に中途で入社、現在はアメーバ事業本部でピグ×スマホ×Webの開発を担当している平松 @co_sche (co-sche)と申します。
家では楽器を弾いて自力で疲れを癒すアラサー男子です。

この連載では、先日社内で行われた「HTML5, CSS3を舐め回す会 Vol.2 - JavaScript Day -」で私がお話した「JavaScriptで始める関数型プログラミング」の内容を元に、記事にまとめたいと思います。

想定している読者は、
  • JavaScriptの基本的な構文を理解している方
  • JavaScriptで関数を定義・使用したことのある方
  • LLと総称される言語を扱っているが、関数型プログラミングを意識したことのない方
です。

はじめに

早速ですが「関数型プログラミング」という字面が長く、タイピングが辛いので所々FP(Functional Programming)と略させて下さい。
同様にJavaScriptもJSと略記します。

さて、昨今「分散並行コンピューティングに向いている」という触れ込みや、「Java7がFP的手法を取り込むか」と思いきや見送りになった件などでFPに注目が集まっています。
が、実はこの手法は特に目新しいものではなく、既に様々な形で私たちの使用している言語に溶け込んでいます。
JSでイベントハンドリングをする際に頻出する、
document.addEventListener('DOMContentLoaded', function(event) {
  console.log('うむ。');
}, false);
あるいは、jQueryを利用した
$(function(){
  console.log('準備はよいな。');
});
というようなコードは、FPのエッセンスをJSが受け継いでいるからこそ可能な芸当なのです。

この連載ではJSに注目して、FPに触れてみましょう。
今回の記事の目的は主に「下ごしらえ」です。

関数型プログラミングのメリットをリストアップ!

プログラマというのは現金なもので、メリットが無いとなかなか学習意欲がわかないものです。

という訳で早速。
  1. コードの見通しが良いよ!
  2. テストしやすいよ!
  3. バグを作りにくいよ!
  4. 思考の道筋が増えるよ!
  5. 楽しいよ!
私個人としては、4と5が特に大きなメリットだと思っています。
今まで、使っていても意識していなかった関数の考え方とその応用を学ぶことになりますので、プログラミングを行う際の思考パターンは確実に増えます。
1,2,3は、連載中(3週目)に実例を挙げて説明させて頂くつもりです。

関数型プログラミングってなんだ

では本題に入りましょう。「FPとはなんぞや」です。Wikipediaによると、
"λ計算の概念をプログラミング言語として体現したものである。すべての計算は関数の評価によって行われる。"
だそうです。
最初の一語が意味不明ですね。でも、ここがポイントです。掘り下げて行きましょう。

λ計算ってなんだ

再びWikipediaによると、
"関数を文字ラムダ (λ) を使った式によって表記する。アロンゾ・チャーチとスティーヴン・コール・クリーネによって1930年代に考案された。"
随分昔に考案されたようですねぇ。
λ式の字面としては、
(λf. (λx. f (λy. x x y)) (λx. f (λy. x x y))) g
と、こんな感じです。

ざっくりと書くと、上記のように表記し、α変換・β簡約(・η変換)といった規則を使って計算を進めていく計算体系です。
数値・演算・集合・制御構造すら関数で表現しようという一風変わった計算体系ですが、チューリング完全であることが証明されています。
これらの記法・変換規則についても連載中に紹介する予定ですが、今はそれほど重要でないので読み流して下さい。

ただ、このλ計算が現在の関数型言語に息づいているということ、もっと言えば「JSの関数式でλ式をそのまま表現できる」ことだけは頭に留めておいて下さい。
ちなみに、上記のλ式をそのままJSの関数式で表現すると、
(function(f) {
  return (function(x) {
    return f(function(y) {
      return x(x)(y);
    });
  })(function(x) {
    return f(function(y) {
      return x(x)(y);
    });
  });
})(g);
となります。

んじゃ、そもそも関数ってなんだ (ココ重要!)

ココ重要です。なので、JSコードを眺めながらその意味をかみしめましょう。

念のため、JavaScriptの関数式の記法をおさらいしておきます。
function(a) { return a + a; } // --- (1)
OKですか?引数aを受け取って、それを2倍した値を返す関数です。

FPの世界で言う関数は「数学的な関数(純粋な関数)」で、値(引数)を食って、計算して、新しい値(戻り値)を吐くものです。
上記(1)は、まさに数学的な関数です。
引数3にこの関数を適用する式は、
(function(a) { return a + a; })(3); // --- (2)
こうです。この式は全体で数値の「6」と評価されます。
是非、お手持ちの実行環境で試してみてください。

この時に重要なのが、「値を食って計算して吐く」以外は関数外の環境に何の影響も与えないということです。
関数外への影響とは、例えばグローバルな変数への再代入や画面への表示、ファイルポインタやネットワークソケットへの書き込みなどです。
上記(1)に副作用はありません。再代入も、画面への表示も明記されていないことを確認して下さい。
処理系によっては、(2)のように評価した際に、任意の出力に評価結果が表示される場合がありますが、それはあくまで処理系の「親切」ですので気にしなくて大丈夫です。

万一影響を与える場合、それを副作用と呼び区別します。厳密なFP(純粋関数型言語)では原則として、副作用は起こりえません。
副作用のあるコードも見ておきましょう。
var a = 0;
(function (b) {
  a = a + b;
  document.write(a);
  return a;
})(4);
結果何が起こるか、どれが副作用か、上の説明から確認してください。

もう一点、数学的な関数は「引数が決まれば戻り値も決まり、何度評価してもこの組み合わせは変わらない」という性質を持ち、これと副作用が無い性質を合わせて参照透過性と呼びます。
例として、Math.sinは参照透過ですが、Math.randは参照透過ではありません。

一般に、副作用を目的とした(gotoのシンタックスシュガーとしての)「サブルーチン」と、上記のような「数学的な関数」をまとめて「関数」と呼ぶ言語が多いですが、これらをしっかり見分けるようにしましょう。

つまり関数型プログラミングとは

すべての計算は(純粋な)関数の評価によって行われる。
つまり、
  • 参照透過ってことは代入はない(必要ない)ぜ!
  • 副作用が無いってことは画面への出力も無いぜ!
  • でもチューリング完全なんだぜ(どやっ)!
ということになります。

うーむ…、それって実用に耐えうるの?

今回のまとめ

上記は関数型プログラミングの大原則ですし、実際にこれに則った(実用的な)言語もあるようですが、この連載ではそんな極端なことは言いません。
事実、多くの「関数型言語」とカテゴライズされる言語では、副作用も代入も可能なようになっています。
まして、DOMの変更やユーザーとの対話を主とするクライアントサイドJSにおいては、副作用なしに何かを成し遂げることは不可能です。

ただ注意深く観察すると、副作用なしに抽象化を行うチャンスは実はたくさん転がってるんです。

ということで、今回の大事なポイントは以下の4つ。
  1. 本来の関数の作用(値を食って計算して吐く)と、副作用(関数外に影響を与える)は意識して区別するようにしよう
  2. 副作用を最小回数・最小範囲に留めよう
  3. やりたいことを抽象化し、モジュール(部品)に分け、関数にまとめる癖をつけよう
  4. FPのエッセンスを自分のプログラミングスタイルに取り込もう
です。
連載中に具体例をお見せする予定ですが、これらのポイントを心がけてプログラミングすることによって、最初に挙げたFPのメリットが活きてきます。

最後に

JavaScriptで始めると銘打っておきながら、今回はJSコードがあまり出て来なかったですね…。
次回はJSコードを交えつつ、残りの下ごしらえ工程を終わらせようと思います。

是非、お楽しみにっ!

--

この連載では偉そうに講釈してますが、実は私も勉強中だったりします。
自分が分かりにくかった点を噛み砕いて可能な限り正確に説明しているつもりですが、万一間違いやトンデモ理論が飛び出した時はAmeba Pocket等ブックマークコメントでビシバシツッコミをいただければ幸いです。


JavaScriptで始める関数型プログラミング 関連記事
JavaScriptで始める関数型プログラミング 1 - 2
JavaScriptで始める関数型プログラミング 1 - 3