関数型プログラミング in javascript
Upcoming SlideShare
Loading in...5
×
 

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

on

  • 5,060 views

jsCafe16(2013/11/10)で話したjavascriptでの関数型プログラミング入門の話です。

jsCafe16(2013/11/10)で話したjavascriptでの関数型プログラミング入門の話です。

Statistics

Views

Total Views
5,060
Views on SlideShare
4,451
Embed Views
609

Actions

Likes
18
Downloads
19
Comments
0

5 Embeds 609

http://oilegg.info 472
https://twitter.com 128
http://timeleap.info 7
http://www.linkedin.com 1
http://s.deeeki.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

  • 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時に思い出す
  • おしまい