ECMAScript6による 
関数型プログラミング 
株式会社トライフォート 
安田裕介
自己紹介 
• 名前:安田裕介 
• Trifortに今年入社の新卒1年生 
• Webフロントエンジニアやってます 
• JavaScript, Scala, C++が好き 
• GitHubアカウント: TanUkkii007
関数型プログラミングとは? 
副作用を排除し関数オブジェクトを駆使する 
プログラミングパラダイム 
関数型プログラミングの2大構成要素 
• 第一級オブジェクトとしての関数 
• イミュータビリティ(不変性) 
拡張性と保守性の高いコードを書く手法として 
近年注目を集めている
JavaScriptと 
関数型プログラミングの関係 
JavaScriptは関数型プログラミング言語である 
Schemeの第一級関数オブジェクトを受け継いだ言語 
ECMAScript6は関数型の以下の機能をも 
可能にする 
1. 変更不可能な変数の宣言 
2. パターンマッチ 
3. 再帰による繰り返し処理 
4. 不変なデータ構造
1.変更不可能な変数の宣言 
• 関数型プログラミングでは値の変更を行わない 
• 変数への再代入を行わない
const宣言子 
再代入できない変数を宣言する 
1 "use strict"; 
2 const foo = "foo"; 
3 
4 foo = “bar”; 
5 //TypeError: foo is read-only 
※strictモードの場合、再代入しようとするとTypeErrorとなる 
※strictモードでない場合、再代入は暗黙に失敗する 
※letと同様ブロックスコープをもつ
2.パターンマッチ 
• 関数型プログラミングではパターンマッチによる値 
の取り出しを行う 
• パターンマッチにより代入などの副作用を減らすこ 
とができる
分割代入(デストラクチャリング) 
配列やオブジェクトからパターンによって値を抽出する 
配列パターンによる抽出 
1 var [a,b] = [1,2];! 
2 console.log(a, b);! 
3 // 1 2! 
4 ! 
5 [b, a] = [a,b];! 
6 ! 
7! 
8 var {name: name, family: {sister: sister}} ! 
分割代入による値の交換 
オブジェクトパターンによる抽出 
! ! ! ! ! ! = {name: 'John Doe', family: {sister: 1}}! 
9 console.log(name, sister);! 
10 // "John Doe" 1 
※for in/ofループや関数の引数でも使えます
3.再帰による繰り返し処理 
• 繰り返し処理の方法として再帰とループの2つの方法がある 
• 再帰の方が代入などの副作用がなく、短く書ける 
• 関数型プログラミングでは繰り返し処理に再帰を使う 
• 末尾呼び出し最適化により再帰でのスタックオーバーフローを回避 
する
末尾呼び出し最適化 
関数呼び出しが末尾呼び出しかどうかを判定し、 
末尾呼び出しの場合、最適化する 
call(call(call(call()))) 
• 関数は呼び出し元に戻るた 
め、その位置を記憶する 
• でもそれが関数本体の末尾 
なら、戻る必要はない 
そこで末尾呼び出し最適化がおきる
例)階乗の計算 
1 // 再帰による階乗計算! 
2 function factorial1(n) {! 
3 if (n === 0)! 
4 return 1;! 
5 return n * factorial1(n - 1);! 
6 }! 
7 ! 
8 //ループによる階乗計算! 
9 function factorial2(n) {! 
10 var result = 1;! 
11 for (var i = 1; i <= n; ++i) {! 
12 result *= i;! 
13 }! 
14 return result;! 
15 }! 
16 ! 
17 factorial1(100000);! 
18 // too much recursion! 
19 factorial2(100000);! 
20 // Infinity 
@k_matsuzaki さんに指摘して 
もらいました。これでは最適化 
されません。正しくは↓ 
←これが末尾呼び出し 
function factorial1(n, acc) { 
if (n == 0) 
return acc; 
return factorial1(n - 1, n * acc); 
末尾呼び出し最適化が実装されれば 
解決される!! 
} 
←再帰ではスタックオーバーフロー 
←ループではInfinityではあるが成功 
factorial1(100000, 1);
4.不変なデータ構造 
• 関数型プログラミングにおける配列やリストなどの 
データ構造は不変であり、自身の値を変更しない
プロキシ 
既存のオブジェクトをラップし、その一部の内部 
メソッドをECMAScriptコードで実装して挙動を 
変えることを可能にするオブジェクト 
プロキシオブジェクトの作り方 
! 
! 
! 
new Proxy(target, { 
get: //proxy[name] 
set: //proxy[name] = val 
apply: //proxy() 
construct: //new Proxy() 
deleteProperty: //delete proxy[name] 
}) 
ラップするターゲットオブジェクト 
内部メソッドに実装を与えるハンドラーオブジェクト 
がおきたときの処理を 
定義できる 
※ハンドラーの各メソッドをトラップという ※トラップは全部で14個ある  ※定義されていないトラップ 
ではデフォルトの挙動が用いられる ※apply, constructトラップは関数がターゲットとなるときのみ有効
例)プロキシによる 
イミュータブルな配列 
関数型プログラミングでは値を変更しない 
ES6のプロキシを使って不変な配列を作ってみよう 
方針 
JavaScriptの配列には自身を変更する破壊メソッドがある 
pop, push, reverse, shift, sort, splice, unshift 
破壊メソッドにsliceを挟むことで非破壊メソッドに変える
通常の配列と同様 
Arrayコンストラクタで 
配列を作れるようにする 
1 var immutable = { 
2 Array: function(...array) { 
3 return immutable.createArray(array); 
4 }, 
5 createArray: function(array = []) { 
6 return new Proxy(array, { 
7 get: function(target, name, receiver) { 
8 var mutator 
9 = immutable.mutators.filter(x => x === name)[0]; 
10 if (mutator) { 
11 return (...args) => { 
12 var copy = target.slice(); 
13 copy[mutator].apply(copy, args); 
14 return immutable.createArray(copy); 
15 }; 
16 } else { 
17 return target[name]; 
18 } 
19 } 
20 }); 
破壊メソッドの判定用の配列 
21 }, 
22 mutators: ["pop","push","reverse","shift","sort","splice","unshift"] 
23 };
1 var immutable = { 
2 Array: function(...array) { 
プロキシを 
3 return immutable.createArray(array); 
4 }, 
5 createArray: function(array = []) { 
6 return new Proxy(array, { 
7 get: function(target, name, receiver) { 
8 var mutator 
9 = immutable.mutators.filter(x => x === name)[0]; 
10 if (mutator) { 
11 return (...args) => { 
12 var copy = target.slice(); 
13 copy[mutator].apply(copy, args); 
14 return immutable.createArray(copy); 
15 }; 
16 } else { 
17 return target[name]; 
18 } 
19 } 
20 }); 
21 }, 
22 mutators: ["pop","push","reverse","shift","sort","splice","unshift"] 
23 }; 
作成しているのはここ
! 
! 
5 createArray: function(array = []) { 
6 return new Proxy(array, { 
ターゲットに配列を使用 
7 get: function(target, name, receiver) { 
8 getトラップ 
var mutator 
9 を定義 
= immutable.mutators.filter(x => x === name)[0]; 
10 if (mutator) { 
11 return (...args) => { 
12 破壊メソッドなら 
var copy = target.slice(); 
13 sliceを挟む関数を返す 
copy[mutator].apply(copy, args); 
14 return immutable.createArray(copy); 
15 }; 
16 } else { 
コピーした配列で 
17 return target[name]; 
プロキシを再作成 
18 } 
19 } 
20 }); 
21 }
プロキシで作った不変配列は 
配列とどう違うのか? 
! 
1 var array = new immutable.Array(1,2,3); 
2 var nativeArray = new Array(1,2,3); 
3 
4 array[array.length - 1]; //3 
5 nativeArray[nativeArray.length - 1]; //3 
6 
7 for (var v of array) console.log(v); //1 2 3 
8 for (var v of nativeArray) console.log(v); //1 2 3 
9 
10 
11 var result = array.push(4).reverse(); //[4,3,2,1] 
12 array === result; //false 
13 array; //[1,2,3] 
14 
15 nativeArray.push(4); 
16 var nativeResult = nativeArray.reverse(); //[4,3,2,1] 
17 nativeResult === nativeResult; //true 
18 nativeArray; //[4,3,2,1] 
←不変配列 
←組み込みの配列 
使い方と挙動に 
違いはない 
不変配列では元の配列は 
変わっていない 
組み込みの配列では 
元の配列は変わっている
まとめ 
関数型の機能をES6でどう実現するかを見てきた 
1. 変更不可能な変数の宣言 
2. パターンマッチ 
3. 再帰による繰り返し処理 
4. 不変なデータ構造 
→ const宣言子 
→ 分割代入 
→ 末尾呼び出し最適化 
→ プロキシ 
ECMAScript6の表現力で 
関数型プログラミングを楽しもう!

ECMAScript6による関数型プログラミング