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

  • 5,127 views
Uploaded on

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

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

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
5,127
On Slideshare
0
From Embeds
0
Number of Embeds
5

Actions

Shares
Downloads
20
Comments
0
Likes
22

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 2013/11/10 jsCafe16 関数型プログラミング in javascript ryuma tsukano
  • 2. 目次 ● ● ● ● 概要 規律 慣例 適用
  • 3. 概要
  • 4. 関数型プログラミングとは? 数学上の関数を用いて プログラミングする考え方の事 そして、言い換えると 規則と慣例ガチガチな理想郷の事
  • 5. 関数とは ここで言う数学上の関数は 普段書いてるプログラムの関数と異なる y = f(x) 上記のfの事。(写像) 入力値xに変換処理をして出力値yを求める
  • 6. 普段の関数と何が違うのか? ● 普段書いてる関数: ○ ○ ○ ○ 入力/出力値と関係無い変数を変える事もある レシーバに破壊的操作を伴う事もある 戻り値が無い事もある 引数以外の変数に依存する事もある ● 関数型の関数: ○ 上に書いた事全部禁止。 ○ y=f(x)の様に入力を出力に変える事だけを行う =>関数型には、厳しい規律と慣例がある
  • 7. 規律と慣例 ● 規律 ○ 変数:変更不可 ○ 関数:参照透過かつ副作用が無い ● 慣例 ○ ○ ○ ○ 第1級/高階関数 無名関数/lambda 再帰 部分適用/カリー化 ※このまとめ方(規律と慣例という分け方)は独自で、人に話しても通じないので、ご注 意を。但し、この中の各キーワードはどの書籍も大体一緒。
  • 8. こんなに厳しくて何が嬉しい? ● 並列処理と相性が良いと言われる ○ 値が入力値にのみ依存してる=状態共有不要 ○ map & reduceは高階関数を参考にしてる ● software固有の複雑性を単純化できる ○ code levelで多くのメリットがある ○ これについて、次のページで詳しく見る。 参考:なぜ関数プログラミングは重要か
  • 9. code levelの幸せ ● 読み易い ● test書き易い ● debugし易い ● Errorが起こり辛い ● 階層が深くならない ● 関数を再利用し易い ...と言われてる。 OOPよりもっと低Layerの話
  • 10. 同じ様な低layarのcoding作法といえば 関数型と関係なく有名な書籍沢山ありますね ● プログラミング作法 ● code complete ● clean code / clean coder ● リーダブルコード...etc... それぞれ指摘はバラバラ。(時代も違うしね) これらと比べ関数型はゴールが明快。
  • 11. 関数型言語と仲間達。 ● 関数型言語(純粋〜非純粋) ○ Haskell/scala/OCaml/LISP/F# etc ■ 言語によりruleの強制度合いが違う ● 関数型「っぽい」言語 ○ ruby/javascript/C# ■ 言語Lvでsupport。例えばrubyはcurryがある ● 手続き型言語 ○ C/java/perl ■ とはいえ関数型的な機能も追加され境目は曖昧
  • 12. なぜjsで関数型? ● jsは、他の言語と比べても特に ○ 安全でない ■ 空値比較周りetc ○ library競合 ■ js/DOM/jQuery/Backbone/underscore etc... ○ 癖強い ■ 生each..etc... これらを 理解し易く整理=>test=>安全に管理する! =>そのために「関数型」の考え方を取り入れる
  • 13. underscore.js ! jsには関数型programmingをやるbaseとして underscoreがある。 ● 基本的な関数揃えている ● browser毎の動作の違い意識しなくて済む ○ ECMAScriptで使える関数増えているが、実際のUserの brower状況を考えると、こういうlibの方が良い ● 有り難い事に、和訳有るみたい。
  • 14. 関数型javascriptで本も出た。 O'Reillyから。 今年’13夏 ※この資料作成でも参考にさせて頂いた ※web link 書籍以外でも、ここ数年、関数型+jsについて blog記事や勉強会でもさかんに扱われてる
  • 15. おすすめリソース ● 書籍 ○ Functional Javascript:jsでどう書くか例いっぱい ○ scalaで学ぶ関数脳入門:関数型言語系で1番分り易い ○ 関数型言語titleの本以外にも、プログラミングの基礎系 の本でも幾つか関数型扱ってる物あり ● webサイト ○ CAさんの記事:基礎から簡単な実例まで。説明丁寧 ○ jsと関数型のgist記事:良記事。Bad pattern記述有 ○ jsカリー化qiita記事:とても丁寧で分かり易い。 ● slideshare ○ 入門(link1,link2) : 面白いし、一通り分かる。 ○ js関数型 : ES6 Arrow funcやTS/自作monad等面白い
  • 16. 規律
  • 17. 規律編から ● ①変数の話 ● ②関数の話 ※但し、実際は規律というより理想。 ● 結局、完全に守る事は出来ない。 ● 出来るだけ守るべき理想の話。
  • 18. 規律①変数は変更できない 変数は変更できない like 定数 ● A)値の変更不可能(immutable) ● B)値の再代入が禁止 ● Aについてjavascriptの変数は ○ string/number = immutable ○ object/arrray = mutable ■ 後者に注意 ● 関数内でも参照渡ししてたら変更不可能 ● Bについてどの型でも再代入は禁止
  • 19. 変数は変更できない 例)元の変数の破壊的操作は禁止 × 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
  • 20. あれ? 引数で参照渡しされたarrayは変更しないけど local変数は変わってるんじゃない?
  • 21. 例外)関数内のlocal変数 関数がimmutableな戻り値を生み出すために、幾 つかのlocal変数を変える事は出来る? ● from http://clojure.org/transients => Yes local変数は関数内で変更して良い ● その方が速度早い場合が多くあるため ○ 教科書通りにいくと、再帰を使うべき ○ だが、実行速度や遠回りな記述を避けるため、例外的に 許可して良い(by oreilly本/scala本) ○ 例えばunderscore内部も結構local変数を変えてる ○ とはいえ可能な限りlocal変数も変化無しが望ましい
  • 22. なぜ変数を変更しないのか? programのバグ等の問題の主な原因は、 気軽に状態を変えてしまう事から来る。 ● 例)function(a) { return 2a * PI ;} ○ 開発時)PI=3.14で面積計算test通過! ○ リリース)PI=誰か別の値入れててerror 逆に状態を変えれない=>安全にcodingできる
  • 23. 実際のmutable mutable(変数変更可能)が便利なのも事実。 ● 例)backbone ○ viewがthis.collectionにある関数をbind ■ Collection変更=>viewで関数を実行! ○ =>コレできないとMVPも成り立たないし 必須でmutableにした方が良い所はそのまま 但し必要以上にmutableにしない方が良い。 =>理想像を描いている。
  • 24. 変数 in 関数型 ちなみに 関数型の名前の通りメインが関数になるので、 変数に何か値を入れる機会は減る 基本は、全部関数に任せる形になる。 =>次は、メインとなるこの関数の話
  • 25. 規律編②関数は参照透過で副作用無し 参照透過性(参照透明)※reference transparency ● どこで関数callしても同じ引数で同じ値返す ○ そのため、どこでも関数を評価値に置換できる ○ 実装した時に戻り値が確定すれば=>bug減る ● 同条件満たす = 参照透過性が高い ○ 一見、当たり前のように聞こえるが... ● 例)右記の例だと ○ 引数が1 =>同じ結果じゃない! ○ 参照透過性は無い。参照不透明 ○ こうならないようにする事 var cnt = 1; function func(num) { return cnt + num; } func(1); // 2 cnt = 100; func(1); // 101 ×
  • 26. 副作用とは? ● 副作用=評価値算出以外の作業 ○ global変数いじる事 ○ fileや画面等に出力する事 ■ document.writeとか... ● 副作用が無い ○ 上記の余計な事をしない事
  • 27. pureな関数 2つの条件 ● 参照透過が高い(input:外部に依存してない) ● 副作用が無い(output:外部に影響しない) これらを満たす関数:pureな関数 満たさない関数:impureな関数 関数型は入出力を独立させてsimpleさを維持する ために、pureな関数を作る事を勧めてる。
  • 28. 理想と現実 但し、pureな関数も理想 ● 副作用(html出力等)無いとweb作れない ● OOPのclassはどうしても参照透過性を破る 大事なのは、 ● impureな関数と ● pureな関数を 分離する事。 ※少しでもpureな関数増やして綺麗にする事
  • 29. 眠くなってきた? 気持ち分かりますりん! 一緒に背伸びするりん! 沢山Keyword出てきて、ドン引きしました? これから、更に沢山のkeyword出てきます。 ※ここで19:50になってたら、今日は終了。 高砂市(たかさご)のゆるキャラ 「ぼっくりん」
  • 30. 慣例
  • 31. 慣例編 関数型で多く見られる慣例的な表現が以下 1. 2. 3. 4. 第1級/高階関数とchain 無名関数/lambda/closure 再帰 部分適用/カリー化 強制力は無いが、書くと「っぽく」なる ○ ちなみに殆ど全部関連付いてる
  • 32. 慣例①第1級/高階関数とchain 例えば。 y = f(x) このxやyいずれかが関数である時、 ● 関数xやy=第1級関数 ※first class function ○ 関数の引数/戻り値いずれかに指定された関数の事 ● 関数f=高階関数 ※higher order function ○ 関数の引数/戻り値いずれかに関数を指定した関数の事
  • 33. 代表的な高階関数(with underscore) map:配列内の値にある操作をして返す filter:配列内から条件を満たす物のみ返す reduce:配列内の値を集計して返す 全部、underscoreで使える。 from http://underscorejs.org/
  • 34. 第一級関数/高階関数を使うメリット 細かく処理を分けれる => 幾つかメリット ● 例) ○ ループ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);
  • 35. 関数のchain 第1級の関数=>関数をchainできる(pipeline) ● ※厳密にはchainingとpipelineは違うが image)関数=>関数=>関数=>... 例) _.compose(_circulate(length), _stepIndex)(index, ((i) -> i+1)) この慣例に沿う事で変数変更最小限にできる
  • 36. 慣例②無名関数/lambda 無名関数(匿名関数) ● 名前の無い関数 ○ ある関数の利用が限定的な時に使う ○ 変数に入れるとlambda関数と呼ぶ ■ var x = function() { … } ○ jsで関数リテラル + 即時関数で無名関数よく見る 関数型では関数を頻繁に書く事になるので、 単発的な関数は無名にするのが望ましい。 ※名前空間を意味なく汚さないため。
  • 37. クロージャ クロージャ:closure ● 無名関数でLexical Scopeに束縛された自由変 数(関数内の引数やlocal変数)を持つ ○ Lexical Scope = 構文で決定できるスコープ ○ 自由変数=引数の時に部分適用の話に続く(後述) ○ jsにprivateが無いので、その代わり function human(num) { var age = 18; return function() { return age; } } human()() // 18
  • 38. 慣例③再帰関数 ある関数の中で自分自身の関数を呼び出す like マトリョーシカ... function add(n) { if(n == 0){ return 0 } else { return n + add(n-1); } }(10) // => 55
  • 39. なぜ再帰を使う必要がある? 例)再帰部を使ってない例 変数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));
  • 40. なぜ再帰を使う必要がある? ループの代わりに再帰 ● 変更してしまう先程のlocal変数無くせた function summRec(array, seed) { if(_.isEmpty(array)) { return seed; } else { return summRec(_.rest(array), _.first(array) + seed); // 末尾再帰(後述) } } summRec(_.range(1,11), 0);
  • 41. 再帰を書く時のコツ 昔からのコツがあるらしい(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));}
  • 42. スタック 関数実行時、スタックに以下が積まれる ● local変数 ● 呼び出し元アドレス ● 関数の引数等 =>関数終了時に解放される =>再帰は解放されない =>stack over flowになる。 RangeError: Maximum call stack size exceeded
  • 43. 末尾再帰最適化 一部の他言語で末尾再帰にすると 最適化されて関数内の毎回のstackを破棄可能 =>stack over flowを回避する。 ● ※末尾再帰=最後に再帰で引数だけで解決 ○ (前例のsumRecがそう。) javascriptは? =>対応してないらしい! =回数やsizeに注意
  • 44. 慣例④部分適用とカリー化 ● 部分適用 ○ 複数の引数を取る関数に、一部の引数のみ値を束縛し た新しい関数を返す操作 ○ 共通の引数を使い回したい時に使う underscoreの_.partial(_.bindも出来る) ● ※_.partialはver1.4.4からなので注意
  • 45. カリー化 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”)
  • 46. 適用
  • 47. 現実的に関数型書くの? 色んなトピックがある ● OOP vs FP ● どこで書けば良いの? ● 自分の書いたソースは関数型なの?
  • 48. OOP vs FP 関数型はOOPと矛盾する所/共通する所がある dataも一々zipするか否か等議論あるが、 そんなに関数型頑張らなくて良いかと。 変数freeze ? そこまでやる? 基本的に、いつも通りのOOPで、 複数のArray/Objectを操作している所で 関数型思い出せばよいのでは?
  • 49. どこで書くのか? シチュエーション ● 複数Model集計/操作/取得処理 ○ ex:Backbone.Collection周り ● library系の関数 ○ ex:決まったjsonのparse処理とか ● Refactoring時に意識するのも効果的 ○ =>次のページへ
  • 50. refactoringと関数型 DOM/jQuery等でsource汚した時、 1. 規律の視点で批判的に見直す 2. 規律で直して慣例表現書けたら書く 程度のcasualな採用で良いのでは。 =>DOM event/汎用的な関数分ける=>test整理=> 切り出した汎用的な関数を再利用=>Happy! ● 良い記事 ○ refactoringの記事 ○ BadPatternまとめ
  • 51. まとめ ● 規律を守る(理想郷を目指す) ○ 変数は変えない ○ 関数は入出力を外部と独立 ● 慣例を幾つか知っておく ○ ○ ○ ○ 高階関数で第一級関数を繋ぐ 無名関数ラムダ関数使う 再帰でloop counterや結果の変数のmutableを回避 部分適用で引数入れた関数を使い回す ● 関数型で無理しない ○ OOPで解決できる問題はOOPでいいんじゃない ○ 複数Model周辺の処理やrefactoring時に思い出す
  • 52. おしまい