狠狠撸

狠狠撸Share a Scribd company logo
2013/11/10 jsCafe16

関数型プログラミング
in javascript
ryuma tsukano
目次
●
●
●
●

概要
规律
惯例
适用
概要
関数型プログラミングとは?

数学上の関数を用いて
プログラミングする考え方の事

そして、言い換えると
規則と惯例ガチガチな理想郷の事
関数とは
ここで言う数学上の関数は
普段書いてるプログラムの関数と異なる
y = f(x)

上記のfの事。(写像)
入力値xに変換処理をして出力値yを求める
普段の関数と何が違うのか?
● 普段書いてる関数:
○
○
○
○

入力/出力値と関係無い変数を変える事もある
レシーバに破壊的操作を伴う事もある
戻り値が無い事もある
引数以外の変数に依存する事もある

● 関数型の関数:
○ 上に書いた事全部禁止。
○ y=f(x)の様に入力を出力に変える事だけを行う
=>関数型には、厳しい规律と惯例がある
规律と惯例
● 规律
○ 変数:変更不可
○ 関数:参照透過かつ副作用が無い

● 惯例
○
○
○
○

第1級/高階関数
無名関数/lambda
再帰
部分适用/カリー化

※このまとめ方(规律と惯例という分け方)は独自で、人に話しても通じないので、ご注
意を。但し、この中の各キーワードはどの書籍も大体一緒。
こんなに厳しくて何が嬉しい?
● 並列処理と相性が良いと言われる
○ 値が入力値にのみ依存してる=状態共有不要
○ map & reduceは高階関数を参考にしてる

● software固有の複雑性を単純化できる
○ code levelで多くのメリットがある
○ これについて、次のページで詳しく見る。

参考:なぜ関数プログラミングは重要か
code levelの幸せ
● 読み易い
● test書き易い
● debugし易い
● Errorが起こり辛い
● 階層が深くならない
● 関数を再利用し易い
...と言われてる。
OOPよりもっと低Layerの話
同じ様な低layarのcoding作法といえば
関数型と関係なく有名な書籍沢山ありますね
● プログラミング作法
● code complete
● clean code / clean coder
● リーダブルコード...etc...

それぞれ指摘はバラバラ。(時代も違うしね)
これらと比べ関数型はゴールが明快。
関数型言語と仲間達。
● 関数型言語(純粋?非純粋)
○ Haskell/scala/OCaml/LISP/F# etc
■ 言語によりruleの強制度合いが違う

● 関数型「っぽい」言語
○ ruby/javascript/C#
■ 言語Lvでsupport。例えばrubyはcurryがある

● 手続き型言語
○ C/java/perl
■ とはいえ関数型的な機能も追加され境目は曖昧
なぜjsで関数型?
● jsは、他の言語と比べても特に
○ 安全でない
■ 空値比較周りetc
○ library競合
■ js/DOM/jQuery/Backbone/underscore etc...
○ 癖強い
■ 生each..etc...

これらを
理解し易く整理=>test=>安全に管理する!
=>そのために「関数型」の考え方を取り入れる
underscore.js !
jsには関数型programmingをやるbaseとして
underscoreがある。
● 基本的な関数揃えている
● browser毎の動作の違い意識しなくて済む
○ ECMAScriptで使える関数増えているが、実際のUserの
brower状況を考えると、こういうlibの方が良い

● 有り難い事に、和訳有るみたい。
関数型javascriptで本も出た。
O'Reillyから。
今年’13夏
※この資料作成でも参考にさせて頂いた

※web link
書籍以外でも、ここ数年、関数型+jsについて
blog記事や勉強会でもさかんに扱われてる
おすすめリソース
● 書籍
○ Functional Javascript:jsでどう書くか例いっぱい
○ scalaで学ぶ関数脳入門:関数型言語系で1番分り易い
○ 関数型言語titleの本以外にも、プログラミングの基礎系
の本でも幾つか関数型扱ってる物あり

● webサイト
○ CAさんの記事:基礎から簡単な実例まで。説明丁寧
○ jsと関数型のgist記事:良記事。Bad pattern記述有
○ jsカリー化qiita記事:とても丁寧で分かり易い。

● slideshare
○ 入門(link1,link2) : 面白いし、一通り分かる。
○ js関数型 : ES6 Arrow funcやTS/自作monad等面白い
规律
规律編から
● ①変数の話
● ②関数の話

※但し、実際は规律というより理想。
● 結局、完全に守る事は出来ない。
● 出来るだけ守るべき理想の話。
规律①変数は変更できない
変数は変更できない like 定数
● A)値の変更不可能(immutable)
● B)値の再代入が禁止
● Aについてjavascriptの変数は
○ string/number = immutable
○ object/arrray = mutable
■ 後者に注意
●

関数内でも参照渡ししてたら変更不可能

● Bについてどの型でも再代入は禁止
変数は変更できない
例)元の変数の破壊的操作は禁止
×
function push1 (array) {
array.push(1);
return array
};
var a = [1,2,3];
var b = push1(a);
console.log(b); // [1,2,3,1]
console.log(a); // [1,2,3,1]

○
function push1 (array) {
var copied = array.slice();
copied.push(1)
return copied;
};
var a = [1,2,3];
var b = push1(a);
console.log(b); // [1,2,3,1]
console.log(a); // [1,2,3]
参考:https://gist.github.com/ympbyc/5564146
あれ?

引数で参照渡しされたarrayは変更しないけど
local変数は変わってるんじゃない?
例外)関数内のlocal変数
関数がimmutableな戻り値を生み出すために、幾
つかのlocal変数を変える事は出来る?
●

from http://clojure.org/transients

=> Yes
local変数は関数内で変更して良い
● その方が速度早い場合が多くあるため
○ 教科書通りにいくと、再帰を使うべき
○ だが、実行速度や遠回りな記述を避けるため、例外的に
許可して良い(by oreilly本/scala本)
○ 例えばunderscore内部も結構local変数を変えてる
○ とはいえ可能な限りlocal変数も変化無しが望ましい
なぜ変数を変更しないのか?
programのバグ等の問題の主な原因は、
気軽に状態を変えてしまう事から来る。
● 例)function(a) { return 2a * PI ;}
○ 開発時)PI=3.14で面積計算test通過!
○ リリース)PI=誰か別の値入れててerror

逆に状態を変えれない=>安全にcodingできる
実際のmutable
mutable(変数変更可能)が便利なのも事実。
● 例)backbone
○ viewがthis.collectionにある関数をbind
■ Collection変更=>viewで関数を実行!
○ =>コレできないとMVPも成り立たないし

必須でmutableにした方が良い所はそのまま
但し必要以上にmutableにしない方が良い。
=>理想像を描いている。
変数 in 関数型
ちなみに
関数型の名前の通りメインが関数になるので、
変数に何か値を入れる機会は減る
基本は、全部関数に任せる形になる。

=>次は、メインとなるこの関数の話
规律編②関数は参照透過で副作用無し
参照透過性(参照透明)※reference
transparency
● どこで関数callしても同じ引数で同じ値返す
○ そのため、どこでも関数を評価値に置換できる
○ 実装した時に戻り値が確定すれば=>bug減る

● 同条件満たす = 参照透過性が高い
○ 一見、当たり前のように聞こえるが...

● 例)右記の例だと
○ 引数が1 =>同じ結果じゃない!
○ 参照透過性は無い。参照不透明
○ こうならないようにする事

var cnt = 1;
function func(num) {
return cnt + num;
}
func(1); // 2
cnt = 100;
func(1); // 101

×
副作用とは?
● 副作用=評価値算出以外の作業
○ global変数いじる事
○ fileや画面等に出力する事
■ document.writeとか...

● 副作用が無い
○ 上記の余計な事をしない事
pureな関数
2つの条件
● 参照透過が高い(input:外部に依存してない)
● 副作用が無い(output:外部に影響しない)
これらを満たす関数:pureな関数
満たさない関数:impureな関数
関数型は入出力を独立させてsimpleさを維持する
ために、pureな関数を作る事を勧めてる。
理想と現実
但し、pureな関数も理想
● 副作用(html出力等)無いとweb作れない
● OOPのclassはどうしても参照透過性を破る
大事なのは、
● impureな関数と
● pureな関数を
分離する事。
※少しでもpureな関数増やして綺麗にする事
眠くなってきた?
気持ち分かりますりん!
一緒に背伸びするりん!

沢山Keyword出てきて、ドン引きしました?
これから、更に沢山のkeyword出てきます。
※ここで19:50になってたら、今日は終了。

高砂市(たかさご)のゆるキャラ
「ぼっくりん」
惯例
惯例編
関数型で多く見られる惯例的な表現が以下
1.
2.
3.
4.

第1級/高階関数とchain
無名関数/lambda/closure
再帰
部分适用/カリー化

強制力は無いが、書くと「っぽく」なる
○ ちなみに殆ど全部関連付いてる
惯例①第1級/高階関数とchain
例えば。
y = f(x)
このxやyいずれかが関数である時、
● 関数xやy=第1級関数 ※first class function
○ 関数の引数/戻り値いずれかに指定された関数の事

● 関数f=高階関数 ※higher order function
○ 関数の引数/戻り値いずれかに関数を指定した関数の事
代表的な高階関数(with underscore)
map:配列内の値にある操作をして返す

filter:配列内から条件を満たす物のみ返す
reduce:配列内の値を集計して返す

全部、underscoreで使える。

from http://underscorejs.org/
第一級関数/高階関数を使うメリット
細かく処理を分けれる => 幾つかメリット
● 例)
○ ループlogicだけ共通化 + 条件だけtest可能に
function filter(array, condition) {
※このあたり、
result = []
underscore + 再帰で
for (var i = 0; i < array.length; i++) {
綺麗に整理できる(後述)
if (condition(array[i])){
result.push(array[i]);
}
}
}
checkEven = function(x){ return x % 2 == 0 }; // 本来Loop内にあった条件判定
checkOdd = function(x) { return x % 2 == 1}; // 処理が、表に出てtestし易い
filter([1,2,3,4,5], checkEven);
filter([1,2,3,4,5], checkOdd);
関数のchain
第1級の関数=>関数をchainできる(pipeline)
● ※厳密にはchainingとpipelineは違うが
image)関数=>関数=>関数=>...
例)
_.compose(_circulate(length), _stepIndex)(index, ((i) -> i+1))

この惯例に沿う事で変数変更最小限にできる
惯例②無名関数/lambda
無名関数(匿名関数)
● 名前の無い関数
○ ある関数の利用が限定的な時に使う
○ 変数に入れるとlambda関数と呼ぶ
■ var x = function() { … }
○ jsで関数リテラル + 即時関数で無名関数よく見る

関数型では関数を頻繁に書く事になるので、
単発的な関数は無名にするのが望ましい。
※名前空間を意味なく汚さないため。
クロージャ
クロージャ:closure
● 無名関数でLexical Scopeに束縛された自由変
数(関数内の引数やlocal変数)を持つ
○ Lexical Scope = 構文で決定できるスコープ
○ 自由変数=引数の時に部分适用の話に続く(後述)
○ jsにprivateが無いので、その代わり
function human(num) {
var age = 18;
return function() { return age; }
}
human()() // 18
惯例③再帰関数
ある関数の中で自分自身の関数を呼び出す
like マトリョーシカ...

function add(n) {
if(n == 0){
return 0
} else {
return n + add(n-1);
}
}(10) // => 55
なぜ再帰を使う必要がある?
例)再帰部を使ってない例
変数result/iを変更してる=>望ましくない
※前述のlocal変数の例外該当するが。
function summ(array) {
var result = 0;
var sz = array.length;
for(var i = 0; i < sz; i++)
result += array[i];
return result;
}
summ(_.range(1,11));
なぜ再帰を使う必要がある?
ループの代わりに再帰
● 変更してしまう先程のlocal変数無くせた

function summRec(array, seed) {
if(_.isEmpty(array)) {
return seed;
} else {
return summRec(_.rest(array), _.first(array) + seed); // 末尾再帰(後述)
}
}
summRec(_.range(1,11), 0);
再帰を書く時のコツ
昔からのコツがあるらしい(1990Touretzky)
1. いつ停止するのか知る事
2. どうstepを取るか決める
○ 再帰部で、問題が分解され小さくなるように

3. step内の小さい問題点を解決する事
○ 基底部で、最小の問題を解決するように

例)右記の関数は
1. _.isEmpty
2. 1+...
3. _.rest()

function myLength(ary) {
if (_.isEmpty(ary))
return 0;
else
return 1 + myLength(_.rest(ary));}
スタック
関数実行時、スタックに以下が積まれる
● local変数
● 呼び出し元アドレス
● 関数の引数等

=>関数終了時に解放される
=>再帰は解放されない
=>stack over flowになる。
RangeError: Maximum call stack size exceeded
末尾再帰最適化
一部の他言語で末尾再帰にすると
最適化されて関数内の毎回のstackを破棄可能
=>stack over flowを回避する。
● ※末尾再帰=最後に再帰で引数だけで解決
○ (前例のsumRecがそう。)

javascriptは?
=>対応してないらしい!
=回数やsizeに注意
惯例④部分适用とカリー化
● 部分适用
○ 複数の引数を取る関数に、一部の引数のみ値を束縛し
た新しい関数を返す操作
○ 共通の引数を使い回したい時に使う

underscoreの_.partial(_.bindも出来る)
● ※_.partialはver1.4.4からなので注意
カリー化 currying
複数の引数をとる関数を、以下の関数にする事
● 引数:元の関数の最初の引数
● 戻り値:元の残りの引数から結果を返す関数
あえてカリー化書く=>部分适用を明示
※curry切出しmax3階層位迄=>それ以上混乱。
function getUserInfo(lang, params) {
return accessAPI(lang, params);}}
getUserInfo(“japanese”, “taro”)
getUserInfo(“japanese”, “hanako”)

function getUserInfo(lang) {
return function(params) {
return accessAPI(lang, params);}}
jpUserInfo = getUserInfo(“japanese”)
jpUserInfo(name: “taro”)
jpUserInfo(name: “hanako”)
适用
現実的に関数型書くの?
色んなトピックがある
● OOP vs FP
● どこで書けば良いの?
● 自分の書いたソースは関数型なの?
OOP vs FP
関数型はOOPと矛盾する所/共通する所がある
dataも一々zipするか否か等議論あるが、
そんなに関数型頑張らなくて良いかと。
変数freeze ? そこまでやる?
基本的に、いつも通りのOOPで、
複数のArray/Objectを操作している所で
関数型思い出せばよいのでは?
どこで書くのか?
シチュエーション
● 複数Model集計/操作/取得処理
○ ex:Backbone.Collection周り

● library系の関数
○ ex:決まったjsonのparse処理とか

● Refactoring時に意識するのも効果的
○ =>次のページへ
refactoringと関数型
DOM/jQuery等でsource汚した時、
1. 规律の視点で批判的に見直す
2. 规律で直して惯例表現書けたら書く
程度のcasualな採用で良いのでは。
=>DOM event/汎用的な関数分ける=>test整理=>
切り出した汎用的な関数を再利用=>Happy!
● 良い記事
○ refactoringの記事
○ BadPatternまとめ
まとめ
● 规律を守る(理想郷を目指す)
○ 変数は変えない
○ 関数は入出力を外部と独立

● 惯例を幾つか知っておく
○
○
○
○

高階関数で第一級関数を繋ぐ
無名関数ラムダ関数使う
再帰でloop counterや結果の変数のmutableを回避
部分适用で引数入れた関数を使い回す

● 関数型で無理しない
○ OOPで解決できる問題はOOPでいいんじゃない
○ 複数Model周辺の処理やrefactoring時に思い出す
おしまい

More Related Content

関数型プログラミング in javascript