最近のJavaScriptって、なんか括弧だらけ!
そう感じている人はいませんか?そういう人はこの記事のターゲットです。もう少し読み進めてください。
私は1年前までそう思っていました。例えば、以下のようなソースです。俗にいう括弧の化物です。
var length = (array) => {
return reduce(map(alwaysReturnOne)(array));
};
length([1,2,3]);
私はずっとJavaの開発をしてきたので、JavaScriptは少し読めればいいやぐらいに思って流していました。しかし、最近ではJavaにもラムダ式と呼ばれる形で括弧の化物が登場してきているのです。SIの現場ではJava8のシンタックス採用は昨年の新規開発案件ぐらいからですが、来年には保守改修などでも使われてくるでしょう。もう、括弧の化物から逃げられないのです。
そのような私が括弧の化物への一歩を踏み出しました。Javaばかりやっていて気づいたらJavaScriptが怖いという人に最初のとっかかりとなれば幸いです。
この記事で書いていること
1. 括弧の正体とは?
2. 括弧を使うと何がいいのか?
1.括弧の正体とは
誤解を恐れずに書くと、括弧の正体は一言で書くと関数です。関数には恒等関数、多項関数、無名関数、定数関数などの種類があります。そして、それを活用する形としてクロージャ、サンク(thunk)、モナド、カリー化などが存在します。こういった理解の難しい概念がコメントもなくソース上で用いられていることが、初学者にとって高い壁になっていると思います。
なので、いったん上記で書いた難しい言葉は忘れてください。但し、括弧は関数である。このことは覚えておいてください。
それでは、括弧(関数)のメリットについて進みましょう。
2.括弧を使うと何がいいのか?
括弧は関数だと先に記載しましたが、「関数とは処理をまとめることによって、共通的に利用できるものである」と考える人が多いと思います。それは確かに一部はあっているのですが、JavaScriptで用いられる関数では少し意味合いが異なります。C言語やJavaでプログラムを書いてきている人はこの考えが強すぎるため、関数型言語としてのJavaScriptが理解しにくいのです。
Java的発想で関数を書くと、業務ロジックを含めた共通処理を関数として定義するでしょう。しかし、それは関数型プログラミングとして考えると、まだまだ関数と呼べる代物でありません。さらに細かく分解していくことで再利用を促したりや冪等性を保つことができるのです。
ファーストクラスオブジェクト
まず、1つだけ覚えてください。関数型言語の特徴を持つJavaScriptの関数は変数に格納できます。これがJavaScriptの関数は第一級オブジェクト(ファーストクラスオブジェクト)と言われる理由です。難しい言葉ですが、「第一級オブジェクト=ただの値(変数)」であると考えてください。
これを理解するだけで、読みにくかったJavaScriptが少し読めるようになります。
例えば、以下のプログラムはconstantという名前の関数を作成しています。また、最後の行ではその関数に実引数"1"を渡してalwaysReturnOneという変数を宣言しています。これにより、alwaysReturnOneには2-4行目の関数が実引数"1"で格納されています。
ここで大事なことはalwaysReturnOneというのは必ず"1"を返す関数であるということなのです。
var constant = (any) => {
return (_) => {
return any;
};
};
var alwaysReturnOne = constant(1);
このように**「関数を格納した変数がいろいろな箇所で使用されている」ことを理解する**ことが、最近のJavaScriptを読み解く一歩になります。
でも、「この関数ってどんなメリットがあるの?」と思うことがあると思います。この単純な関数でも使いみちははあります。以下のプログラムを実行すると、配列の要素をすべて1に変換することができます。これはmapという配列の中身を処理する関数に、さきほどの全てを1にする関数を引数渡ししているためです。
[1,2,3].map(alwaysReturnOne);
// 結果-> [1,1,1]
JavaScriptを読んでいく時には、その内部で使われている変数は関数である場合があり、関数である場合は、その関数をブロック({})単位で見ていくことで、意外とすんなりと読んでいくことができます。
上記のコードにさらにcountという要素の値を合算していくメソッドと組み合わせることで、[1,1,1]の配列の要素を足し算し、配列のサイズを計算する関数を作成できます。
var length = (array) => {
return count(map(alwaysReturnOne)(array));
};
length([1,2,3]);
//結果 → 3
(reduceとmapはUnderscore.jsなどのライブラリを使用するほうが簡単ですが、関数型の学習ということで自作しています。2017/12/7追記)
//map関数
var map = (convert) => {
return (array) => {
return array.reduce((accumulator, data) => {
return accumulator.concat(convert(data));
},[]);
};
};
//count関数
var count = (array) => {
return array.reduce((accumulator, data) => {
return accumulator + data;
},0);
};
JavaScriptの関数について整理すると、JavaScriptでは関数は値と同じように扱えるため、以下のような特徴をもつことができます。
- 関数を引数に持つことができる
- 関数を返す関数がつくれる
この仕組みを高階関数と呼び、JavaScriptでは多用されるため、結果として括弧が多くなります。
しかし、それはソースを読みづらくするものではなく、プログラムの再利用性を高めたり、障害への耐性を高めることに効果を発揮します。
最近はバックエンドにSpring、フロントエンドにはAngularという組み合わせも増えてきていますのでJavaScriptを食わず嫌いの人はぜひ取り組んでみてはどうでしょうか?
(AngularはTypeScriptを使用することが多いですが。。)
参考書籍
私は学習するのに以下の3冊の本を使っています。関数型プログラミングというのはこれまでJavaを書いてきた人が体験していないパラダイムシフトだと思いますので、1冊でわからなければ2冊、それでも理解が難しければ3冊目という方法がよいのかなと思います。近道はないですし、本の中身は全て理解する必要はないと思いますが、書籍を変えると新たな視座もあるのでお薦めします。
関数型プログラミングの基礎 JavaScriptを使って学ぶ
JavaScriptで学ぶ関数型プログラミング
JavaScript関数型プログラミング 複雑性を抑える発想と実践法を学ぶ impress top gearシリーズ