【topotal輪読会】javascript で学ぶ関数型プログラミング 4 章

83
JavaScriptで学ぶ 関数型プログラミング 4章 高階関数 小式澤 篤 (@lastarrow21) Topotal 輪読会 2015%02%11 KOSHIKIZAWAAtsushi (2015402411) 第二回Topotal輪読会 1

Upload: atsushi-koshikizawa

Post on 18-Jul-2015

2.136 views

Category:

Internet


0 download

TRANSCRIPT

Page 1: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

JavaScriptで学ぶ関数型プログラミング第4章"高階関数小式澤!篤!(@lastarrow21)

Topotal'輪読会

2015%02%11

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 1

Page 2: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

出典• JavaScriptで学ぶ関数型プログラミング

• Michael/Fogus/著

• 和田/祐一郎/訳

• O'REILLY/®/オライリー・ジャパン/出版

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 2

Page 3: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

前置き

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 3

Page 4: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

前置き• 自由変数をわかりやすくするために、変数名を全て大文字表記にしている

• 一般的なプログラミングスタイルでは推奨されない

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 4

Page 5: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

(復習)

クロージャ3.5.1%節

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 5

Page 6: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

クロージャ• プログラム内で環境を共有する仕組み

• スコープの実行完了後でも内部情報を保持し続ける関数function makeAdder(CAPTURED) { // CAPTURED : 自由変数 (確保される変数)

return function(free) { return free + CAPTURED };}var add10 = makeAdder(10); // add10 が"10"を確保add10(32); // => 42

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 6

Page 7: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4章"高階関数

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 7

Page 8: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

高階関数• 前提

• 第一級のデータ型である

• 次のうち、少なくともどちらか一方を満たす

• 引数として関数をとる

• 関数を戻り値として返す

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 8

Page 9: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

高階関数• 前提

• 第一級のデータ型である

• 次のうち、少なくともどちらか一方を満たす

• 引数として関数をとる"!"まずはここから考える

• 関数を戻り値として返す

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 9

Page 10: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4.1$引数として関数を取る関数

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 10

Page 11: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

引数として関数を取る関数• 既に(4章よりも前に)登場している

• _.map、_.reduce、_.filter

• 関数を引数に取ることに関して、もう少し詳しく考える

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 11

Page 12: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4.1.1$関数を渡すことを考えるmax、finder、best

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 12

Page 13: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「関数を渡す」と何ができるのか• 配列などから最大値を返す関数"_.max

console.log(_.max([1, 2, 3, 4, 5]));// => 5

• 第二引数に、関数を渡すことができる"!"高階関数var people = [{ name : "Fred", age : 65 }, { name : "Lucy", age : 36 }];console.log(_.max(people, function(p){ return p.age }));// => {name : "Fred", age : 65}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 13

Page 14: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

_.maxの考察• 関数を引数に取れる

• 任意のオブジェクト同士を比較する手段を提供している

• 関数を構築する上で有用

• 常に">"(大なり)を用いて比較している

• この制限により、本当に関数型であるとはいえない

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 14

Page 15: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「良い」値を返す関数finder• 第1,$第2引数は関数

• 比較するデータから比較できるように値を作る関数

• 2つの値を比較して、そこから「良い」値を返す関数

• 第3引数は比較するデータを含むオブジェクトの配列

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 15

Page 16: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「良い」値を返す関数finderの実装function finder(valueFun, bestFun, coll) { return _.reduce(coll, function(best, current) { var bestValue = valueFun(best); var currentValue = valueFun(current);

return (bestValue === bestFun(bestValue, currentValue)) ? best : current; });}

finder(_.identity, Math.max, [1, 2, 3, 4, 5]); // => 5

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 16

Page 17: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「良い」値を返す関数finderの他の用例• プロパティ名ageを比較して最大のものを「良い」とするfinder(plucker('age'), Math.max, people); // => { name : "Fred", age : 65 }

• プロパティ名nameの最初の文字がLであるものを「良い」とする

finder(plucker('name'), function(x, y){ return (x.charAt(0) === "L") ? x : y }, people);// => { name : "Lucy", age : 36 }

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 17

Page 18: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「良い」値を返す関数finderの考察• 比較の仕方を関数で指定できる

• 様々なタイプの「最良の値を探す」関数を生成できる

• ロジックを複製している箇所がある// finder関数内return (bestValue === bestFun(bestValue, currentValue)) ? best : current;// 「最良の値を探す」bestFun関数内return (x.charAt(0) === "L") ? x : y;

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 18

Page 19: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

ロジックの複製を回避するために• 最良の値を判定する関数は

• 最初の引数が2つ目の引数よりも良い場合にtrueを返す

• 渡された引数を比較できる形まで紐解く方法を知っている

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 19

Page 20: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

よりエレガントな関数bestの実装function best(fun, coll){ return _.reduce(coll, function(x, y){ return fun(x, y) ? x : y; });}

console.log(best(function(x, y){ return x > y; }, [1, 2, 3, 4, 5]));// => 5

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 20

Page 21: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

より良い値を探す関数の考察• 2つの関数を引数に取る関数#finder

• ロジックの重複が発生した

• ロジックの重複を削除することに一般化した関数#best

• 引き締まった

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 21

Page 22: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

より一般的な関数に作り直すには4.1.2%関数を渡すことを更に考える

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 22

Page 23: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

指定した回数だけ値を格納する関数!repeat

function repeat(times, VALUE) { return _.map(_.range(times), function() { return VALUE; });}

repeat(4, "Major");// => ["Major", "Major", "Major", "Major"]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 23

Page 24: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!repeat!の考察• とても素直な実装

• 「一般的な繰り返し」とは言えない

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 24

Page 25: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!repeat!の考察• とても素直な実装

• 「一般的な繰り返し」とは言えない

• 計算を繰り返したいことがある

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 25

Page 26: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!repeat!の考察• とても素直な実装

• 「一般的な繰り返し」とは言えない

• 計算を繰り返したいことがある

「値ではなく関数を使え」

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 26

Page 27: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

繰り返し計算した結果を格納する関数!repeatedly

function repeatedly(times, fun) { return _.map(_.range(times, fun);}

repeatedly(3, function() { return Math.floor(Math.random() * 10 + 1);});// => [1, 3, 8]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 27

Page 28: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!repeatedly!の考察• 関数を呼ぶことによって任意の値が格納されるようになった

• もちろん固定値も格納できる

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 28

Page 29: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!repeatedly!の考察• 関数を呼ぶことによって任意の値が格納されるようになった

• もちろん固定値も格納できるrepeatedly(3, function() { return "Odeley!"; });// => { "Odeley!", "Odeley!", "Odeley!" }

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 29

Page 30: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!repeatedly!の考察• 関数を呼ぶことによって任意の値が格納されるようになった

• もちろん固定値も格納できるrepeatedly(3, function() { return "Odeley!"; });// => { "Odeley!", "Odeley!", "Odeley!" }

• 引数に関係なく固定値を返す関数はよくある話(詳しくは5章にて)

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 30

Page 31: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「値ではなく関数を使え」(再掲)

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 31

Page 32: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「値ではなく関数を使え」• 関数"repeatedly"で、格納される値が関数によって任意に生成されるようになった

• ただし、反復回数がいつもわかるとは限らない// for文による例for (let i = 2; i <= 1024; i = i * 2) { console.log(i);} // => 2 4 8 16 32 64 128 256 512 1024 // 10回繰り返されたKOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 32

Page 33: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

さらに進化した関数!iterateUntil

• 第一引数"fun

• 格納する値を生成する関数

• 第二引数"check

• 繰り返しが終了すべき時にfalseを返す関数

• 第三引数"init

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 33

Page 34: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

さらに進化した関数!iterateUntil

function iterateUntil(fun, check, init) { var ret = []; var result = fun(init); while (check(result)) { ret.push(result); result = fun(result); } return ret;}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 34

Page 35: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

iterateUntil!の使用例iterateUntil(function(n) { return n * 2; }, function(n) { return n <= 1024; }, 1);// => [ 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 ]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 35

Page 36: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!iterateUntil!の考察• 繰り返し条件が関数"check"の結果依存になった

• 実質無制限

• 終了条件だけ知っている場合も用いれる!!関数!repeatedly!の1段階上の存在となった

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 36

Page 37: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

高階関数• 前提

• 第一級のデータ型である

• 次のうち、少なくともどちらか一方を満たす

• 引数として関数をとる

• 関数を戻り値として返す"!"次はこっち

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 37

Page 38: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4.2$他の関数を返す関数

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 38

Page 39: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

今までの関数を返す関数• makeAdder

• 自由変数と引数の和を返す関数を返す

• complement

• 引数にとった関数の真偽値判定が逆になる関数を返す

• plucker

• 引数にとった値をキーとして対応する値を返す関数を返すKOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 39

Page 40: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

定数を返す関数!always

• 関数"repeatedly"では引数を無視して値を返す関数を使った

• 定数を返す関数":"k"と呼ばれる

• ここでは"always"と呼ぶfunction always(VALUE) { return function() { return VALUE; };}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 40

Page 41: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

クロージャ使用上の注意!(1/2)

• 関数は常に唯一の値を生成する

• 自由変数VALUEに束縛された戻り値は常に等しい'[Braithwaite,'

'13]

var f = always(function(){});f() === f(); // => true

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 41

Page 42: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

クロージャ使用上の注意!(2/2)

• 新しく生成されたクロージャは、それぞれ異なる値を返すvar f = always(function(){});var g = always(function(){});f() === g(); // => false

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 42

Page 43: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!always!の使用例• 無名関数の代わりに使うとコードがすっきりするrepeatedly(3, always("Odelay!"));//=> [ "Odelay!", "Odelay!", "Odelay!" ]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 43

Page 44: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!invoker

• 第一引数"NAME

• 第二引数のMETHODを実行する対象

• 第二引数"METHOD

• 実行するメソッド名

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 44

Page 45: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!invoker!の実装function invoker (NAME, METHOD) { return function(target /* args ... */) { if (!existy(target)) fail("Must provide a target");

var targetMethod = target[NAME]; var args = _.rest(arguments);

return doWhen((existy(targetMethod) && METHOD === targetMethod), function() { return targetMethod.apply(target, args); }); };}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 45

Page 46: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!invoker!の使用例var rev = invoker('reverse', Array.prototype.reverse);

_.map([[1,2,3]], rev);//=> [ [ 3,2,1 ] ]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 46

Page 47: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!invoker!の考察• invoker"が返す関数はクロージャ

• reverse"などを保持しておいて、後で使用する"(revの設定データ)

• 関数"always"とは違って、特別な処理を行う関数を返す

• 関数型プログラミングでは、オブジェクトを引数に取ることを好む

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 47

Page 48: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4.2.1%引数を高階関数に確保する• Ques&on.*なぜ関数を返す関数を生成するの?

• Hint.*高階関数に渡す引数は、返される関数の設定項目var add100 = makeAdder(100);add100(38); // => 138

• makeAdder関数に100という値を設定したものをadd100という名前に束縛した

• ここからは、変数を確保した関数を返す関数の話KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 48

Page 49: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4.2.2$大義のために変数を確保する

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 49

Page 50: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

例!:!ユニークに文字列を生成する関数

素朴な実装の場合function uniqueString(len) { return Math.random().toString(36).substr(2, len);};

uniqueString(10);//=> liydklvjbk

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 50

Page 51: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

ランダム文字列に接頭辞を付けたくなったら?

関数!uniqueString!を修正するfunction uniqueString(prefix) { return [prefix, new Date().getTime()].join('');};

uniqueString("argento");//=> "argento1356107740868"

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 51

Page 52: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

また要件が変わった!

• 要件":"指定した文字列に連番をつけたものfunction makeUniqueStringFunction(start) { var COUNTER = start; // suffixの開始番号

return function(prefix) { return [prefix, COUNTER++].join(''); }};

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 52

Page 53: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!makeUniqueStringFunction!の実行例// 連番は0からスタートvar uniqueString = makeUniqueStringFunction(0);

uniqueString("dari");//=> "dari0"

uniqueString("dari");//=> "dari1"

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 53

Page 54: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

オブジェクトによる!makeUniqueStringFunction!の再実装

var generator = { count: 0, uniqueString: function(prefix) { return [prefix, this.count++].join(''); }};generator.uniqueString("bohr"); // => bohr0generator.uniqueString("bohr"); // => bohr1

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 54

Page 55: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

オブジェクト!generator!の危険性• count"プロパティに再代入すると"!

generator.counter = "gotcha";console.log(generator.uniqueString("bohr"));// --> bohrNaN

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 55

Page 56: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

オブジェクト!generator!の改良var omgenerator = ( function(init) { var COUNTER = init; return { uniqueString: function(prefix) { return [prefix, COUNTER++].join(''); } }})(0);print(omgenerator.uniqueString("lichking-"));

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 56

Page 57: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

オブジェクト!omgenerator!の考察• COUNTER"の隠蔽には成功した

• 非常に美しくない

• ECMAScript.next"では頑張っている

• makeUniqueStringFunction"のようにクロージャを使えば美しい

• 参照透明性がないKOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 57

Page 58: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

参照透明性• 関数が返す結果がその引数によってのみ左右されること

• makeUniqueStringFunction"が返す値は自身の実行回数依存

• 何回呼び出したかは知ることは不可能

• 詳しくは7章で

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 58

Page 59: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「存在しない状態」var nums = [1,2,3,null,5];_.reduce(nums, function(total, n) { return total * n });//=> 0

doSomething({ WhoCares: 42, critical: null });

//!!"

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 59

Page 60: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

「存在しない状態」に備える関数!fnull

• 「存在しない状態」に対する防御のための関数

• fnull"の引数は関数と1つ以上のデフォルト値

• fnull"が返す関数の引数に"null"や"undefined"が混ざっていたら、デフォルト値がかわりに用いられる

• デフォルト値の代入は、必要なときが来るまで遅延される

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 60

Page 61: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!fnull!の実装function fnull(fun /*, default value */) { var defaults = _.rest(arguments); return function(/* args */) { var args = _.map(arguments, function(e, i) { return existy(e) ? e : defaults[i]; }); return fun.apply(null, args); };}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 61

Page 62: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!fnull!の使用例(1)!:!数値 null

var nums = [1,2,3,null,5];var safeMult = fnull( function(total, n) { return total * n }, 1, 1);_.reduce(nums, safeMult);//=> 30

• nullはデフォルト値"1"に置換される

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 62

Page 63: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!fnull!の使用例(2)!:!設定オブジェクトfunction defaults(d) { return function(o, k) { var val = fnull(_.identity, d[k]); return o && val(o[k]); };}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 63

Page 64: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!defaults!の利用function doSomething(config) { var lookup = defaults({critical: 108}); return lookup(config, 'critical');}

doSomething({critical: 9});//=> 9

doSomething({});//=> 108

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 64

Page 65: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!fnull!の考察• 危険な値(undefinedやnullなど)を気の利いたデフォルト値に変更できる

• o[k] || someDefault$とか書かなくても良くなる

• doSomething$関数と離れた場所で引数のチェックを行える

• 引数の検証ロジックをカプセル化できた

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 65

Page 66: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4.3$すべてを結集オブジェクトバリデータ

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 66

Page 67: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4.3$すべてを結集オブジェクトバリデータ• 任意の基準に基いて妥当性を検証する

• 流暢なものがいい

• パーツから構成したい

• 発生したエラーを全て報告して欲しい

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 67

Page 68: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!checker

• 真偽値を返す関数を引数にとる

• false"が返ってきたら、エラーメッセージを追加する

• 真偽値を返す関数の"message"フィールドを探す

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 68

Page 69: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!checker!の実装function checker(/* validators */) { var validators = _.toArray(arguments);

return function(obj) { return _.reduce(validators, function(errs, check) { if (check(obj)) return errs; else return _.chain(errs).push(check.message).value(); }, []); };}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 69

Page 70: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!checker!の使用例• エラーメッセージが擬似メタデータとして付与された特殊用途の検証用関数

• 一般的な解決方法ではないが、使える

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 70

Page 71: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!checker!の使用例var alwaysPasses = checker(always(true), always(true));alwaysPasses({}); // => []

var fails = always(false);fails.message = "人生における過ち";var alwaysFails = checker(fails);alwaysFails({}); // => [ "人生における過ち" ]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 71

Page 72: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

message!プロパティを与えることの問題• 検証用関数を生成する度に"message"を与えるのは面倒

• もとからある"message"プロパティを削除する可能性がある

• _message"としても良いが、今度は"_message"思い出さなければならない

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 72

Page 73: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!validator

• message"プロパティを与えることの問題を解決する高階関数function validator(message, fun) { var f = function(/* args */) { return fun.apply(fun, arguments); };

f['message'] = message; return f;}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 73

Page 74: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!validator!の使用例var gonnaFail = checker(validator("ZOMG!", always(false)));console.log(gonnaFail(100));// => [ "ZOMG!" ]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 74

Page 75: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

検証用関数を分離して定義する利点• 検証用関数にわかりやすい名前を付与できるfunction aMap(obj) { reutrn _.isObject(obj);}var checkCommand = checker(validator("マップデータである必要があります", aMap));checkCommand({}); // => []checkCommand(42); // => [ "マップデータである必要があります" ]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 75

Page 76: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

流暢な検証関数• 関数を返す関数に渡す引数は、返される関数の動作を「設定」するもの

• 例:#必要なキーの単純なリストは美しく流暢

• hasKeys('msg', 'type')とか

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 76

Page 77: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!hasKeys!の実装function hasKeys(/* 検証したいキーのリスト */) {

var KEYS = _.toArray(arguments); var fun = function(obj) { return _.every(KEYS, function(k) { return _.has(obj, k); }); }; fun.message = cat(["Must have values for keys:"], KEYS).join(" "); return fun;}

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 77

Page 78: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!hasKeys!の特徴• 自由変数"KEYS"を確保したクロージャが妥当性検証を行う

• hasKeys"関数は"fun"関数実行時の設定を行う

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 78

Page 79: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

関数!hasKeys!の使用例var checkCommand = checker(validator("must be a map", aMap), hasKeys('msg', 'type'));

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 79

Page 80: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

checkCommand!の使用例

checkCommand({msg:0"blah",0type:0"display"});//0=>0[0]checkCommand(32);//0=>0[0"マップデータである必要があります",0"Must0have0values0for0

keys:0msg0type"0]checkCommand({});//0=>0[0"Must0have0values0for0keys:0msg0type"0]

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 80

Page 81: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4.4#まとめ

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 81

Page 82: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

高階関数の特徴!(再掲)

• 前提

• 第一級のデータ型である

• 次のうち、少なくともどちらか一方を満たす

• 引数として関数をとる

• 関数を戻り値として返す

KOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 82

Page 83: 【Topotal輪読会】JavaScript で学ぶ関数型プログラミング 4 章

4章"高階関数"まとめ• 関数を他の関数に渡した

• iterateUntil"などを例に、関数に関数を渡したほうが良い場合を示した

• 関数が関数を返す関数を定義した

• 予期しない値に対する防御策"fnull

• 強力な制約関数"checker,"validator,"hasKeysKOSHIKIZAWA)Atsushi)(2015402411))第二回Topotal輪読会 83