$30 off During Our Annual Pro Sale. View Details »

Node, Promises and Functional Programming

Node, Promises and Functional Programming

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

Yuta Okamoto

April 25, 2013
Tweet

More Decks by Yuta Okamoto

Other Decks in Programming

Transcript

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

    View Slide

  2. ୭ʁ
    • Ԭຊ ༤ଠ (@okapies)
    • Java/Scala ϓϩάϥϚ
    • ؔ਺ܕϓϩάϥϛϯάमߦத…
    • ٕज़จॻͷ຋༁Λ͍͔ͭ͘΍ͬͯ·͢
    • Effective Scala (http://twitter.github.io/effectivescala/
    index-ja.html)
    • “Scala Conference in Japan 2013” ຋༁νʔϜ

    View Slide

  3. ܦҢ

    View Slide

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

    View Slide

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

    View Slide

  6. Future/Promise͕େ׆༂

    View Slide

  7. ͦΜͳ͋Δ೔…

    View Slide

  8. View Slide

  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/

    View Slide

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

    View Slide

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

    View Slide

  12. ௒͕Μ͹ͬͯಡഁ

    View Slide

  13. 㱺 ἤΒΕΔ

    View Slide

  14. ͏͓͒͒…ʢඞࢮʣ

    View Slide

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

    View Slide

  16. Oh...

    View Slide

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

    View Slide

  18. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. // fs.readFile(filename, callback)
    fs.readFile('file1.txt',
    // ͠͹Β͕࣌ؒ͘ܦͭͱ...
    function(error, buffer) {
    // ݁Ռ͕ඈͼग़ͯ͠ݱΕΔ
    }
    };
    ίʔϧόοΫ

    View Slide

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

    View Slide

  24. ”ωετ஍ࠈ (Pyramid of Doom)”͸ຊ࣭Ͱ͸ͳ͍
    step1(function (value1) {
    step2(value1, function(value2) {
    step3(value2, function(value3) {
    step4(value3, function(value4) {
    // ...
    });
    });
    });
    });

    View Slide


  25. View Slide

  26. ྫ: ෳ਺ͷϑΝΠϧΛฒྻతʹ fs.stat ͯ͠
    mtime ΛऔΓग़͢ɻ
    file1
    file2
    file3
    stat mtime
    stat
    stat
    mtime
    mtime
    ฒྻॲཧ
    શͯͷ݁Ռ͕
    ἧͬͨΒ࣍΁

    View Slide

  27. 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!

    View Slide

  28. ผͷॲཧΛ
    ෇͚଍͍ͨ͠…

    View Slide

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

    View Slide

  30. 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 ΛऔΓग़͢ॲཧΛ௥Ճ

    View Slide

  31. ਖ਼͍͠ॱংͰɺ͔ͭޮ཰ྑ͘ॲཧͰ͖Δ
    ίʔυΛॻ͖͍ͨɻ
    file1
    file2
    file3
    stat mtime
    stat
    stat
    mtime
    mtime
    size
    ֤ϑΝΠϧ͸Ұ౓͚ͩΞΫηε͢Δ
    file1 ͕औಘͰ͖ͨΒશͯͷstat
    ͕ἧͬͯͳͯ͘΋ॲཧ͢Δ

    View Slide

  32. ੍໿৚݅Λ͖ͪΜͱ
    ຬͨ͢ίʔυΛ
    ॻ͘ͱ͜͏ͳΔ

    View Slide

  33. 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...

    View Slide

  34. • ॲཧΛϞδϡʔϧԽͨ͠Γ૊ΈཱͯͨΓ͢Δͷ͕೉͍͠ɻ
    • ஞ࣍ॲཧ͔ฒྻॲཧ͔Λ໌ࣔతʹ੍ޚ͢Δඞཁ͕͋Δɻ
    • ਖ਼͘͠ಈ͘ίʔυΛॻ͘ͷ͕େมɻ
    • ઃܭͷҙਤ͕໌֬Ͱͳ͘ϝϯςφϯεੑ͕௿͍ɻ
    • ॲཧͷ࣮ߦઓུʢஞ͔࣍ฒྻ͔ʣ࣍ୈͰɺޙଓͷλεΫΛ
    ௥Ճ͢Δํ๏͕มΘͬͯ͠·͏ɻ

    View Slide

  35. ίʔϧόοΫ vs. ϓϩϛε

    View Slide

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

    View Slide

  37. var p1 = new Promise();
    p1.then(console.log);
    ...
    p1.resolve(42);
    ϓϩϛε͕ղܾ͞Εͨ࣌
    ʹݺͿؔ਺Λొ࿥
    ϓϩϛεΛղܾ͢Δ
    㱺 ͜͜Ͱ͸ console ʹ “42” ͕ग़ྗ͞ΕΔ

    View Slide

  38. ͱ͜ΖͰ…

    View Slide

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

    View Slide

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

    View Slide

  41. ϓϩϛε × ؔ਺ܕ

    View Slide

  42. ؔ਺ܕͬͯͳ͋ʹʁ

    View Slide

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

    View Slide

  44. ໋ྩܕϓϩάϥϛϯά
    • Ͳ͏΍Δ͔ (how) Λɺ໋ྩγʔέϯεΛ໌ࣔతʹهड़
    ͯ͠Ϛγϯʹ఻͑Δɻ
    ؔ਺ܕϓϩάϥϛϯά
    • ԿΛͯ͠΄͍͔͠ (what) Λ”஋ಉ࢜ͷؔ܎”ʹΑͬͯه
    ड़͠Ϛγϯʹ఻͑Δɻ
    • ίϯϐϡʔλ͸”஋ಉ࢜ͷؔ܎”͔Β໋ྩγʔέϯεΛ
    ܭࢉ͢Δɻ

    View Slide

  45. ؔ਺
    ஋ಉ࢜ͷґଘؔ܎Λ
    දݱ͢Δಓ۩
    ʹ

    View Slide

  46. function func(in) {
    return /* in ͔Β out Λܭࢉ */;
    }
    out = func(in)
    ”out ͸ in ʹґଘ͍ͯ͠Δ”ͱಡΊΔ
    ؔ਺
    ஋Λೖྗͯ͠ɺม׵ͯ͠ɺ஋Λग़ྗ͢Δ
    ग़ྗ͸ೖྗʹʢͷΈʣґଘ͢Δ
    ʹ

    View Slide

  47. ෳ਺ͷؔ਺Λ߹੒ͯ͠
    ΑΓେ͖ͳؔ਺Λ࡞Δ

    View Slide

  48. ؔ਺ͷग़ྗΛ
    ผͷؔ਺ͷೖྗ΁ͱܨ͛ͯ
    ॲཧͷύΠϓϥΠϯΛ࡞Δ
    function f(in) { return /* in ͔Β out Λܭࢉ */ }
    function g(in) { return /* in ͔Β out Λܭࢉ */ }
    out = g(f(in))
    f ͷग़ྗΛ g ʹೖྗ͢Δ
    ಉ༷ʹ”out ͸ in ʹґଘ͍ͯ͠Δ”ͱಡΊΔ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. 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 ͷ഑ྻ͔Βϓϩϛεͷ഑ྻΛ࡞Δ

    View Slide

  53. // 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;
    };
    }
    ίʔϧόοΫ൛͔Βϓϩϛε൛Λ࡞Δϔϧύʔؔ਺

    View Slide

  54. ผͷॲཧΛ
    ෇͚Ճ͍͑ͨ࣌͸ʁ

    View Slide

  55. var statsPromises = paths.map(fs_stat);
    statsPromises[0].then(function(stat) { /* stat.size */ });
    then() Λ࢖͏ͱϓϩϛεʹґଘ͢Δ
    ผͷඇಉظॲཧΛهड़Ͱ͖Δ
    file1 stat size
    ϓϩϛεΛ࡞Δ
    fs_stat
    then(function(stat)->...)
    ശ͔Β஋ΛऔΓग़͠
    ؔ਺ʹ༩͑Δ

    View Slide

  56. then() ͸ඇಉظܭࢉͷ݁ՌΛλΠϛϯάඇ
    ґଘͰѻ͑Δɻthen() ʹ౉͞ΕΔؔ਺͸ɺ
    • ஋͕͍ͭར༻ՄೳʹͳΔ͔
    • λεΫΛͲΜͳॱ൪Ͱॲཧ͢Δ͔
    Λؾʹ͢Δඞཁ͕ͳ͍ɻ

    View Slide

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

    View Slide

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

    View Slide

  59. શͯͷ stat ͕ἧ͔ͬͯΒॲཧ͍ͨ͠…
    file1
    file2
    file3
    stat mtime
    stat
    stat
    mtime
    mtime
    ݁Ռ͕ἧ͏ͷΛ଴͍ͪͨ

    View Slide

  60. stat mtime
    stat
    stat
    mtime
    mtime
    ”ϓϩϛεͷϦετ”Λ
    ”Ϧετʹର͢Δϓϩϛεʹ”ม׵
    stat
    stat
    stat
    var statsPromises = paths.map(fs_stat);
    list(statsPromises).then(function(stats) { /* stats */ });
    ଴ͪ߹Θ͕ͤՄೳʹʂ
    list() then()

    View Slide

  61. // 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() Λ௥Ճ͢Δϔϧύʔؔ਺

    View Slide

  62. ·ͱΊΔͱ…

    View Slide

  63. 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
    ґଘؔ܎ͷهड़ͷΈͰ
    ஞ࣍ॲཧ΋ฒྻॲཧ΋࣮ݱͰ͖͍ͯΔʂ

    View Slide

  64. A. λεΫಉ࢜ͷґଘؔ܎Λߟ͑Δɻ
    B. ඞཁͳૢ࡞ͷॱংΛߟ͑Δɻ
    C.BΛ࣮ݱ͢Δ੍ޚϑϩʔΛॻ͘ɻ
    ϓϩϛεΛ࢖͏ͱɺίϯϐϡʔλ͕
    A ͔Β࠷దͳ C Λಋग़ͯ͘͠ΕΔʂ

    View Slide

  65. ϓϩϛε͸೉͍͠ʁ

    View Slide

  66. Mikeal Rogersࢯͷ൓࿦
    • ”ϓϩϛεΛ࢖ͬͨ API ͸ɺͦΕ͕Ͳ͏
    ಈ࡞͢Δ͔Λௐ΂Δඞཁ͕͋Δɻ”
    • ”then() ΍ list() ͸ҙຯ࿦ΛӅṭ͢Δͷ
    Ͱɺೝ஌తෛՙ͕େ͖͍ɻ”
    from http://www.futurealoof.com/posts/broken-promises.html

    View Slide

  67. James Coglanࢯͷ࠶൓࿦
    • ”ϓϩϛε͕ easy Ͱ͋Δ͔͸ॏཁͰͳ
    ͍ɻsimple Ͱ͋Δ͔Λ໰͏΂͖ɻ”
    • ”΍Γ͍ͨ͜ͱ͕มΘͬͨ࣌ʹίʔυͷ
    มߋ͕গͳ͘ࡁΉɻϓϩϛε͸ɺίϨ
    Ϋγϣϯ΍ඇಉظੑ΍࣮ߦॱংͱ͍ͬ
    ͨ֓೦ͱ෼཭Ͱ͖Δ͔Βͩɻ”
    from http://blog.jcoglan.com/2013/04/01/callbacks-promises-and-simplicity/

    View Slide

  68. ʢࢲͷҙݟʣ
    • ”ίετͷࢧ෷͍Λੵۃతʹ༛༧ͭͭ͠
    ίʔσΟϯάͰ͖Δ”͜ͱ͸ϓϩτλΠ
    ϐϯά౳Ͱ΋ॏཁͳੑ࣭ɻ
    • ίετͱϝϦοτΛఱṝʹ͔͚Δɻ
    • ͨͩɺඇಉظϓϩάϥϛϯάͷ”ίετ
    ͷࢧ෷͍ظݶ”͸ҙ֎ͱૣ͍ͷͰ͸ʁ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide