Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Node, Promises and Functional Programming

Node, Promises and Functional Programming

東京Node学園 8時限目 ”Nodeとプロミスと、時々、関数型” by @okapies
http://connpass.com/event/2125/

Ef45a84b10aa3cb5acd2ea0c9846bd3b?s=128

Yuta Okamoto

April 25, 2013
Tweet

Transcript

  1. Nodeͱϓϩϛεͱ ࣌ʑɺؔ਺ܕ @okapies

  2. ୭ʁ • Ԭຊ ༤ଠ (@okapies) • Java/Scala ϓϩάϥϚ • ؔ਺ܕϓϩάϥϛϯάमߦத…

    • ٕज़จॻͷ຋༁Λ͍͔ͭ͘΍ͬͯ·͢ • Effective Scala (http://twitter.github.io/effectivescala/ index-ja.html) • “Scala Conference in Japan 2013” ຋༁νʔϜ
  3. ܦҢ

  4. Java VMͷ ੩తܕ෇͖ݴޠ ΦϒδΣΫτࢦ޲ʴؔ਺ܕ

  5. ඇಉظRPCϥΠϒϥϦ”Finagle” Scala ͷ Node.jsʂ Tumblr, 4sq౳΋ར༻ from http://twitter.github.io/finagle/ Twitter͕։ൃ

  6. Future/Promise͕େ׆༂

  7. ͦΜͳ͋Δ೔…

  8. None
  9. “Callbacks are imperative, promises are functional” from http://blog.jcoglan.com/2013/03/30/callbacks-are-imperative-promises-are-functional-nodes-biggest-missed-opportunity/

  10. ஶऀ: James Coglan͞Μ • ΠΪϦεͷ Ruby/JavaScript ϓϩάϥϚ • Haskell΍ؔ਺ܕϓϩάϥϛϯάʹৄ͍͠ •

    FayeɺHeistɺJS.Classͷ࡞ऀ
  11. هࣄͷཁ఺ • ”Node.js͸ίʔϧόοΫͰͳ͘ϓϩϛεΛ࠾ ༻͢Δ΂͖ͩͬͨ”ͱओுɻ • ϓϩϛεϞσϧ͕༏Ε͍ͯΔཧ༝Λɺ๛෋ͳ ۩ମྫΛݩʹղઆɻ • ͱͯ΋௕͍

  12. ௒͕Μ͹ͬͯಡഁ

  13. 㱺 ἤΒΕΔ

  14. ͏͓͒͒…ʢඞࢮʣ

  15. ΋͏ɺΰʔϧͯ͠΋͍͍ΑͶ…ʁ from https://gist.github.com/okapies/5354929

  16. Oh...

  17. ຊ೔͸͓ট͖͍͖ͨͩ ͋Γ͕ͱ͏͍͟͝·͢ m( _ _ )m

  18. None
  19. ඇಉظϓϩάϥϛϯά

  20. ඇಉظॲཧΛॻ࣌͘ʹ ԿΛߟ͑Δʁ A. λεΫಉ࢜ͷґଘؔ܎Λߟ͑Δɻ B. ඞཁͳૢ࡞ͷॱংΛߟ͑Δɻ C.BΛ࣮ݱ͢Δ੍ޚϑϩʔΛॻ͘ɻ

  21. ίʔϧόοΫ vs. ϓϩϛε ※Ҏ߱ͷίʔυྫ͸جຊతʹ ”Promises are functional...” ΑΓҾ༻ɻ

  22. // fs.readFile(filename, callback) fs.readFile('file1.txt', // ͠͹Β͕࣌ؒ͘ܦͭͱ... function(error, buffer) { //

    ݁Ռ͕ඈͼग़ͯ͠ݱΕΔ } }; ίʔϧόοΫ
  23. ίʔϧόοΫͷྑ͘ͳ͍ͱ͜Ζ • ॲཧΛϞδϡʔϧԽͨ͠Γ૊ΈཱͯͨΓ ͢Δͷ͕೉͍͠ɻ • ஞ࣍ॲཧ͔ฒྻॲཧ͔Λ໌ࣔతʹ੍ޚ͢ Δඞཁ͕͋Δɻ

  24. ”ωετ஍ࠈ (Pyramid of Doom)”͸ຊ࣭Ͱ͸ͳ͍ step1(function (value1) { step2(value1, function(value2) {

    step3(value2, function(value3) { step4(value3, function(value4) { // ... }); }); }); });
  25. ྫ: ෳ਺ͷϑΝΠϧΛฒྻతʹ fs.stat ͯ͠ mtime ΛऔΓग़͢ɻ file1 file2 file3 stat

    mtime stat stat mtime mtime ฒྻॲཧ શͯͷ݁Ռ͕ ἧͬͨΒ࣍΁
  26. var async = require('async'), fs = require('fs'); var paths =

    ['file1.txt', 'file2.txt', 'file3.txt']; async.map(paths, fs.stat, function(error, stats) { // stats Λ࢖͏ }); file1 file2 file3 stat mtime stat stat mtime mtime OK!
  27. ผͷॲཧΛ ෇͚଍͍ͨ͠…

  28. file1 ͷ size Λ࢖͏ॲཧΛ௥Ճ͢Δɻ file1 file2 file3 stat mtime stat

    stat mtime mtime size
  29. var paths = ['file1.txt', 'file2.txt', 'file3.txt']; async.map(paths, fs.stat, function(error, stats)

    { // stats Λ࢖͏ }); fs.stat(paths[0], function(error, stat) { // stat.size Λ࢖͏ }); file1 file2 file3 stat mtime stat stat mtime mtime file1 stat size ೋ౓ΞΫηεͯͯ͠ޮ཰͕ѱ͍… file1 ͷ size ΛऔΓग़͢ॲཧΛ௥Ճ
  30. ਖ਼͍͠ॱংͰɺ͔ͭޮ཰ྑ͘ॲཧͰ͖Δ ίʔυΛॻ͖͍ͨɻ file1 file2 file3 stat mtime stat stat mtime

    mtime size ֤ϑΝΠϧ͸Ұ౓͚ͩΞΫηε͢Δ file1 ͕औಘͰ͖ͨΒશͯͷstat ͕ἧͬͯͳͯ͘΋ॲཧ͢Δ
  31. ੍໿৚݅Λ͖ͪΜͱ ຬͨ͢ίʔυΛ ॻ͘ͱ͜͏ͳΔ

  32. var paths = ['file1.txt', 'file2.txt', 'file3.txt'], file1 = paths.shift(); async.parallel([

    function(callback) { fs.stat(file1, function(error, stat) { // stat.size Λ࢖͏ callback(error, stat); }); }, function(callback) { async.map(paths, fs.stat, callback); } ], function(error, results) { var stats = [results[0]].concat(results[1]); // stats Λ࢖͏ }) Oh...
  33. • ॲཧΛϞδϡʔϧԽͨ͠Γ૊ΈཱͯͨΓ͢Δͷ͕೉͍͠ɻ • ஞ࣍ॲཧ͔ฒྻॲཧ͔Λ໌ࣔతʹ੍ޚ͢Δඞཁ͕͋Δɻ • ਖ਼͘͠ಈ͘ίʔυΛॻ͘ͷ͕େมɻ • ઃܭͷҙਤ͕໌֬Ͱͳ͘ϝϯςφϯεੑ͕௿͍ɻ • ॲཧͷ࣮ߦઓུʢஞ͔࣍ฒྻ͔ʣ࣍ୈͰɺޙଓͷλεΫΛ

    ௥Ճ͢Δํ๏͕มΘͬͯ͠·͏ɻ
  34. ίʔϧόοΫ vs. ϓϩϛε

  35. ϓϩϛεͱ͸ • ”׬ྃͯ͠ͳ͍͔΋͠Εͳ͍ܭࢉ”ͷ݁ Ռ͕ೖͬͯΔശɻ • ϓϩϛεࣗମ΋஋ͳͷͰɺؔ਺ͷؒͰ ड͚౉͠Ͱ͖Δɻ • ܭࢉ͕׬ྃ͢ΔͱಛఆͷॲཧΛݺͼग़ ͯ͘͠ΕΔػೳΛ࣋ͭ৔߹͕ଟ͍ɻ

  36. var p1 = new Promise(); p1.then(console.log); ... p1.resolve(42); ϓϩϛε͕ղܾ͞Εͨ࣌ ʹݺͿؔ਺Λొ࿥

    ϓϩϛεΛղܾ͢Δ 㱺 ͜͜Ͱ͸ console ʹ “42” ͕ग़ྗ͞ΕΔ
  37. ͱ͜ΖͰ…

  38. ͱ͍͏λΠτϧͰ͕͢ ͢΂ͯͷϓϩϛε͕ ؔ਺ܕͳΘ͚Ͱ͸͋Γ·ͤΜ

  39. ݴޠ΍ϥΠϒϥϦʹΑͬͯେ͖͘ػೳ͕ҧ͏ • Πϕϯτ௨஌ػೳ͕͋Δ͔ • ߹੒Ͱ͖Δ͔ • ಡΈࠐΈઐ༻දݱ͕͋Δ͔ • ΤϥʔϋϯυϦϯάͰ͖Δ͔ ໊લ΋༷ʑ:

    • Future • Promise • Deferred • I-var • …
  40. ϓϩϛε × ؔ਺ܕ

  41. ؔ਺ܕͬͯͳ͋ʹʁ

  42. ؔ਺ܕϓϩάϥϛϯά ͋ΒΏΔ΋ͷΛ஋ͱͯ͠ ѻ͏͜ͱͰ໰୊Λղ͘ख๏ ʹ ”ࣜࢦ޲”ͱ΋ݺ͹ΕΔ

  43. ໋ྩܕϓϩάϥϛϯά • Ͳ͏΍Δ͔ (how) Λɺ໋ྩγʔέϯεΛ໌ࣔతʹهड़ ͯ͠Ϛγϯʹ఻͑Δɻ ؔ਺ܕϓϩάϥϛϯά • ԿΛͯ͠΄͍͔͠ (what)

    Λ”஋ಉ࢜ͷؔ܎”ʹΑͬͯه ड़͠Ϛγϯʹ఻͑Δɻ • ίϯϐϡʔλ͸”஋ಉ࢜ͷؔ܎”͔Β໋ྩγʔέϯεΛ ܭࢉ͢Δɻ
  44. ؔ਺ ஋ಉ࢜ͷґଘؔ܎Λ දݱ͢Δಓ۩ ʹ

  45. function func(in) { return /* in ͔Β out Λܭࢉ */;

    } out = func(in) ”out ͸ in ʹґଘ͍ͯ͠Δ”ͱಡΊΔ ؔ਺ ஋Λೖྗͯ͠ɺม׵ͯ͠ɺ஋Λग़ྗ͢Δ ग़ྗ͸ೖྗʹʢͷΈʣґଘ͢Δ ʹ
  46. ෳ਺ͷؔ਺Λ߹੒ͯ͠ ΑΓେ͖ͳؔ਺Λ࡞Δ

  47. ؔ਺ͷग़ྗΛ ผͷؔ਺ͷೖྗ΁ͱܨ͛ͯ ॲཧͷύΠϓϥΠϯΛ࡞Δ function f(in) { return /* in ͔Β

    out Λܭࢉ */ } function g(in) { return /* in ͔Β out Λܭࢉ */ } out = g(f(in)) f ͷग़ྗΛ g ʹೖྗ͢Δ ಉ༷ʹ”out ͸ in ʹґଘ͍ͯ͠Δ”ͱಡΊΔ
  48. ؔ਺ܕϓϩάϥϛϯά •͋ΒΏΔ΋ͷΛ”஋”ͱͯ͠ѻ͏ɻ •஋ಉ࢜ͷґଘؔ܎Λ”ؔ਺”Ͱදݱ͢Δɻ •ෳ਺ͷؔ਺Λ߹੒ͯ͠ΑΓେ͖ͳؔ਺Λ࡞Δɻ ʹ

  49. ϓϩϛε × ؔ਺ܕ •͋ΒΏΔϓϩϛεΛ”஋”ͱͯ͠ѻ͏ɻ •ϓϩϛεಉ࢜ͷґଘؔ܎Λ”ؔ਺”Ͱදݱ͢Δɻ •ෳ਺ͷඇಉظܭࢉΛ߹੒ͯ͠ΑΓେ͖ͳඇಉظܭ ࢉΛ࡞Δɻ ʹ

  50. ϓϩϛεΛ࢖ͬͯΈΔ

  51. var fs_stat = promisify(fs.stat); var paths = ['file1.txt', 'file2.txt', 'file3.txt'];

    var statsPromises = paths.map(fs_stat); file1 file2 file3 stat stat stat fs_stat ݁Ռ͸ϓϩϛε ͱ͍͏ശʹೖΔ String ͷ഑ྻ͔Βϓϩϛεͷ഑ྻΛ࡞Δ
  52. // promisify :: (a -> (Error -> b -> ())

    -> ()) -> (a -> Promise b) var promisify = function(fn, receiver) { return function() { var slice = Array.prototype.slice, args = slice.call(arguments, 0, fn.length - 1), promise = new Promise(); args.push(function() { var results = slice.call(arguments), error = results.shift(); if (error) promise.reject(error); else promise.resolve.apply(promise, results); }); fn.apply(receiver, args); return promise; }; } ίʔϧόοΫ൛͔Βϓϩϛε൛Λ࡞Δϔϧύʔؔ਺
  53. ผͷॲཧΛ ෇͚Ճ͍͑ͨ࣌͸ʁ

  54. var statsPromises = paths.map(fs_stat); statsPromises[0].then(function(stat) { /* stat.size */ });

    then() Λ࢖͏ͱϓϩϛεʹґଘ͢Δ ผͷඇಉظॲཧΛهड़Ͱ͖Δ file1 stat size ϓϩϛεΛ࡞Δ fs_stat then(function(stat)->...) ശ͔Β஋ΛऔΓग़͠ ؔ਺ʹ༩͑Δ
  55. then() ͸ඇಉظܭࢉͷ݁ՌΛλΠϛϯάඇ ґଘͰѻ͑Δɻthen() ʹ౉͞ΕΔؔ਺͸ɺ • ஋͕͍ͭར༻ՄೳʹͳΔ͔ • λεΫΛͲΜͳॱ൪Ͱॲཧ͢Δ͔ Λؾʹ͢Δඞཁ͕ͳ͍ɻ

  56. ϓϩϛεͱ then() Λ࢖͏ͱ… • ίϯϐϡʔλ͸ɺهड़͞Εͨґଘؔ܎Λ ݩʹ࣮ߦॱংΛࣗಈతʹܾఆ͢Δɻ • ख࡞ۀͰ੍ޚϑϩʔΛॻ͘ඞཁ͕ͳ͍ʂ

  57. Ϧετʹର͢Δ ϓϩϛε

  58. શͯͷ stat ͕ἧ͔ͬͯΒॲཧ͍ͨ͠… file1 file2 file3 stat mtime stat stat

    mtime mtime ݁Ռ͕ἧ͏ͷΛ଴͍ͪͨ
  59. stat mtime stat stat mtime mtime ”ϓϩϛεͷϦετ”Λ ”Ϧετʹର͢Δϓϩϛεʹ”ม׵ stat stat

    stat var statsPromises = paths.map(fs_stat); list(statsPromises).then(function(stats) { /* stats */ }); ଴ͪ߹Θ͕ͤՄೳʹʂ list() then()
  60. // list :: [Promise a] -> Promise [a] var list

    = function(promises) { var listPromise = new Promise(); for (var k in listPromise) promises[k] = listPromise[k]; var results = [], done = 0; promises.forEach(function(promise, i) { promise.then(function(result) { results[i] = result; done += 1; if (done === promises.length) promises.resolve(results); }, function(error) { promises.reject(error); }); }); if (promises.length === 0) promises.resolve(results); return promises; }; ϓϩϛεͷϦετʹ then() Λ௥Ճ͢Δϔϧύʔؔ਺
  61. ·ͱΊΔͱ…

  62. var fs_stat = promisify(fs.stat); var paths = ['file1.txt', 'file2.txt', 'file3.txt'],

    statsPromises = list(paths.map(fs_stat)); statsPromises[0].then(function(stat) {/* stat.size Λ࢖͏ */}); statsPromises.then(function(stats) {/* stats Λ࢖͏ */}); file1 file2 file3 list(paths.map(fs_stat)) stat stat stat size mtime mtime mtime ґଘؔ܎ͷهड़ͷΈͰ ஞ࣍ॲཧ΋ฒྻॲཧ΋࣮ݱͰ͖͍ͯΔʂ
  63. A. λεΫಉ࢜ͷґଘؔ܎Λߟ͑Δɻ B. ඞཁͳૢ࡞ͷॱংΛߟ͑Δɻ C.BΛ࣮ݱ͢Δ੍ޚϑϩʔΛॻ͘ɻ ϓϩϛεΛ࢖͏ͱɺίϯϐϡʔλ͕ A ͔Β࠷దͳ C Λಋग़ͯ͘͠ΕΔʂ

  64. ϓϩϛε͸೉͍͠ʁ

  65. Mikeal Rogersࢯͷ൓࿦ • ”ϓϩϛεΛ࢖ͬͨ API ͸ɺͦΕ͕Ͳ͏ ಈ࡞͢Δ͔Λௐ΂Δඞཁ͕͋Δɻ” • ”then() ΍

    list() ͸ҙຯ࿦ΛӅṭ͢Δͷ Ͱɺೝ஌తෛՙ͕େ͖͍ɻ” from http://www.futurealoof.com/posts/broken-promises.html
  66. James Coglanࢯͷ࠶൓࿦ • ”ϓϩϛε͕ easy Ͱ͋Δ͔͸ॏཁͰͳ ͍ɻsimple Ͱ͋Δ͔Λ໰͏΂͖ɻ” • ”΍Γ͍ͨ͜ͱ͕มΘͬͨ࣌ʹίʔυͷ

    มߋ͕গͳ͘ࡁΉɻϓϩϛε͸ɺίϨ Ϋγϣϯ΍ඇಉظੑ΍࣮ߦॱংͱ͍ͬ ͨ֓೦ͱ෼཭Ͱ͖Δ͔Βͩɻ” from http://blog.jcoglan.com/2013/04/01/callbacks-promises-and-simplicity/
  67. ʢࢲͷҙݟʣ • ”ίετͷࢧ෷͍Λੵۃతʹ༛༧ͭͭ͠ ίʔσΟϯάͰ͖Δ”͜ͱ͸ϓϩτλΠ ϐϯά౳Ͱ΋ॏཁͳੑ࣭ɻ • ίετͱϝϦοτΛఱṝʹ͔͚Δɻ • ͨͩɺඇಉظϓϩάϥϛϯάͷ”ίετ ͷࢧ෷͍ظݶ”͸ҙ֎ͱૣ͍ͷͰ͸ʁ

  68. ͓࿩Ͱ͖ͳ͔ͬͨࣄ • Ϧετॲཧ (reduce) ͱͷ૊Έ߹Θͤ • ஗ԆϓϩϛεʹΑΔґଘੑղܾ • Promises/A+ ͱϞφυ

    • Fantasy Land ͔Β Real World ΁
  69. • ίʔϧόοΫ͸࣮ߦॱংΛ໌ࣔ͢Δͷ Ͱͱ͖ͬͭ΍͍͢ɻ • มߋʹڧ͘ɺϞδϡϥʔͳϓϩάϥϜ Λॻ͖͍ͨͳΒϓϩϛε͕͓קΊɻ • ੍ޚϑϩʔ͸ػցʹߟ͑ͤ͞Α͏ʂ ·ͱΊ

  70. ϝοηʔδ • ؔ਺ܕɺษڧͯ͠Έ·ͤΜ͔ʁ • ྫ͑࢓ࣄͰ࢖Θͳͯ͘΋ɺϓϩάϥϚ ͱͯܳ͠෩Λ޿͛Δͷʹେ͍ʹ໾ཱͭ ͱࢥ͍·͢ɻ

  71. ࢁຊ࿨඙, ”ؔ਺ϓϩάϥϛϯά͕ڭ͑ͯ͘ΕΔن཯” ؾܰʹֶΜͰΈ͍ͨਓ޲͚:

  72. Miran Lipovaa, “͍͢͝Haskellͨͷֶ͘͠΅͏!”ʢΦʔϜࣾʣ ΨνͰ΍Γ͍ͨਓ޲͚: