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

kitchen-async: a promising (?) Promise library, or a poor man's core.async

OHTA Shogo
January 29, 2018

kitchen-async: a promising (?) Promise library, or a poor man's core.async

2018/01/29のLisp meetup #60の発表資料です。

OHTA Shogo

January 29, 2018
Tweet

More Decks by OHTA Shogo

Other Decks in Programming

Transcript

  1. LJUDIFOBTZODBQSPNJTJOH

    1SPNJTFMJCSBSZ
    PSBQPPSNBO`TDPSFBTZOD
    -JTQNFFUVQ
    !BUIPT

    View Slide

  2. ࣗݾ঺հ
    ‣ 5XJUUFS!BUIPT
    ‣ (JU)VCBUIPT
    ‣ $MPKVSF 4DSJQU
    ॻ͍ͯ·͢
    3FBDU/BUJWFΞϓϦΛ$MPKVSF4DSJQUͰ
    "MFYB4LJMMΛ$MPKVSF4DSJQUͰ

    View Slide

  3. 5-%3
    ‣ $MPKVSF$MPKVSF4DSJQUͰඇಉظϓϩάϥϛϯάͱ͍͏ͱ
    DPSFBTZOD͕༗໊͕ͩৗʹ࠷ྑͷબ୒ࢶͱ͸ݶΒͳ͍
    ‣ +BWB4DSJQU &$."4DSJQU
    ͸1SPNJTF΍BTZODBXBJU
    Λݴޠ࢓༷ʹऔΓೖΕ͍ͯͯɺ+4ͱͷJOUFSPQΛߟ͑Δͱ
    1SPNJTFΛʮ͏·͘ʯѻ͑Δ͜ͱ΋ඞཁ
    ‣ 1SPNJTFͱ΋DPSFBTZODͱ΋͏·͘΍͍ͬͯͨ͘Ίͷϥ
    ΠϒϥϦLJUDIFOBTZODΛ࡞ͬͨɺͱ͍͏࿩

    View Slide

  4. ࠓ೔࿩͢͜ͱ
    ‣ എܠ
    +BWB4DSJQUք۾ͷඇಉظϓϩάϥϛϯάࣄ৘
    $MPKVSF4DSJQUք۾ͷඇಉظϓϩάϥϛϯάࣄ৘
    ‣ $MPKVSF4DSJQUͰͷඇಉظϓϩάϥϯάͷ໰୊
    ‣ LJUDIFOBTZODͷ঺հ

    View Slide

  5. എܠ

    View Slide

  6. +BWB4DSJQUք۾ͷඇಉظࣄ৘
    ‣ +BWB4DSJQUͷଟ͘ͷॲཧܥ͸γϯάϧεϨου
    ‣ *0ॲཧ౳ͷ׬ྃΛಉظతʹ଴ͪ߹Θͤͯ͠·͏ͱɺͦͷ
    εϨου͸ॲཧ׬ྃ·ͰԿ΋Ͱ͖ͳ͘ͳΔ
    ‣ *0ͷ׬ྃ଴ͪ౳ͷؒεϨουΛଞͷॲཧʹ໌͚౉͢͜ͱ
    ͰεϨουΛޮ཰తʹ࢖͑ΔΑ͏ʹͳΔ
    ˠඇಉظϓϩάϥϛϯά

    View Slide

  7. ίʔϧόοΫ
    ‣ ॲཧ͕׬ྃͨ͠ޙʹ࣮ߦ͢ΔॲཧΛ౉͢
    ‣ +BWB4DSJQUͱͯ͠͸΋ͬͱ΋ݪ࢝తͳඇಉظ"1*ͷ࡞Γํ
    ‣ ඇಉظॲཧ͕ଟஈʹͳΔͱίʔϧόοΫ͕ωετͯ͠ίʔυͷ
    Մಡੑ͕ѱ͘ͳΔ ͍ΘΏΔʮίʔϧόοΫ஍ࠈʯ

    const fs = require(‘fs’)
    fs.readFile(‘input.txt’, (err, data) => {
    console.log(data)
    })

    View Slide

  8. 1SPNJTF
    ‣ 1SPNJTF͸ඇಉظͳܭࢉ݁ՌΛอ࣋͢Δσʔλܕ
    ‣ ίʔϧόοΫΛҾ਺ʹड͚Δ୅ΘΓʹ1SPNJTFΛฦ͢Α͏
    ʹ͓ͯ͘͠ͱʜ
    const readFileAsync = (path) => {
    return new Promise((resolve, reject) => {
    fs.readFile(path, (err, data) => {
    if (err) return reject(err)
    resolve(data)
    })
    })
    }

    View Slide

  9. 1SPNJTF
    ‣ ॲཧͷ׬ྃޙʹ࣮ߦ͢ΔॲཧΛޙ͔ΒઃఆͰ͖Δ
    ‣ ϝιουνΣʔϯͰଟஈͷॲཧΛܨ͛ΒΕΔ
    readFileAsync(‘input.txt’)
    .then((text) => {
    return JSON.parse(text)
    })
    .then((json) => {
    console.log(json)
    })
    .catch((err) => {
    console.log(err)
    })

    View Slide

  10. BTZODBXBJU
    ‣ 1SPNJTFΛ࢖ͬͨॲཧΛ௚઀ܗࣜͰ͔͋ͨ΋ಉظతͳॲཧͷΑ͏ʹ
    هड़Ͱ͖Δ
    ‣ ଟ͘ͷ৔߹1SPNJTFΛ௚઀࢖͏ΑΓ੍ޚϑϩʔ͕෼͔Γ΍͘͢ͳΔ
    (async () => {
    try {
    const text =
    await readFileAsync(‘input.txt’)
    return JSON.parse(text)
    } catch (err) {
    console.log(err)
    }
    })()

    View Slide

  11. +BWB4DSJQUք۾ͷඇಉظࣄ৘
    ‣ &$."4DSJQU &4
    ͷ࢓༷΁ͷऔΓࠐΈ
    &41SPNJTF
    &4BTZODBXBJU
    ‣ 8FCඪ४ͷ"1*Ͱͷར༻
    'FUDI"1*
    8FC35$ͷϝσΟΞऔಘ
    4FSWJDF8PSLFS FUDFUD
    ‣ αʔυύʔςΟͷOQNϞδϡʔϧͰͷར༻
    +BWB4DSJQUͷ༷ʑͳ"1*ʹ1SPNJTF͕࢖ΘΕͭͭ͋Δ

    View Slide

  12. $MPKVSF4DSJQUք۾ͷඇಉظࣄ৘
    ‣ ΄΅DPSFBTZODҰ୒ͳงғؾ
    ‣ DPSFBTZOD͸$41 ڠௐεϨουͱνϟωϧΛհͨ͠ϝο
    ηʔδύογϯά
    ΛϞσϧʹ͍ͯ͠Δ
    ‣ 1SPNJTFͱಉ༷ͷ࢖͍ํ΋Ͱ͖Δ͕ɺͭͷνϟωϧ͔Β
    ෳ਺ճ஋Λड͚औͬͨΓɺ஋ΛૹΓฦͤΔ෼ΑΓڧྗ

    View Slide

  13. DPSFBTZODͰͷඇಉظॲཧ
    (require ‘[clojure.core.async :as a])
    (defn read-file [path]
    (let [ch (a/chan)]
    (fs.readFile path
    (fn [err data]
    (a/put! ch (or err data))))
    ch))
    (a/go
    (let [data (a/(js/console.log data)))

    View Slide

  14. ར༻ऀ͔Βͷ͓೰Έ
    ʮ$MPKVSF4DSJQU͔Βͩͱ1SPNJTFΛѻ͏ίʔυ
    Λ͖ͬ͢Γॻ͚·ͤΜʯ ୅உੑձࣾһ

    ˞͜Ε͸ݸਓͷײ૝Ͱײ͡ํʹ͸ݸਓ͕ࠩ͋Γ·͢

    View Slide

  15. ར༻ऀ͔Βͷ͓೰Έ
    ʮ$MPKVSF4DSJQU͔Βͩͱ1SPNJTFΛѻ͏ίʔυ
    Λ͖ͬ͢Γॻ͚·ͤΜʯ ୅உੑձࣾһ

    (-> (readFileAsync “input.txt”)
    (.then (fn [text]
    (js/JSON.parse text)))
    (.then (fn [json]
    (js/console.log json)))
    (.catch (fn [err]
    (js/console.log err))))
    ˞͜Ε͸ݸਓͷײ૝Ͱײ͡ํʹ͸ݸਓ͕ࠩ͋Γ·͢

    View Slide

  16. ར༻ऀ͔Βͷ͓೰Έ
    ʮ$MPKVSF4DSJQU͔Βͩͱ1SPNJTFΛѻ͏ίʔυ
    Λ͖ͬ͢Γॻ͚·ͤΜʯ ୅உੑձࣾһ

    (-> (puppeteer/launch)
    (.then (fn [browser]
    (-> (.newPage browser)
    (.then (fn [page]
    (.then (.goto page “https://www.google.com")
    #(.screenshot page
    #js{:path “image.png"}))))
    (.then #(.close browser)))))))
    ˞͜Ε͸ݸਓͷײ૝Ͱײ͡ํʹ͸ݸਓ͕ࠩ͋Γ·͢

    View Slide

  17. ར༻ऀ͔Βͷ͓೰Έ
    ‣ ͦ΋ͦ΋1SPNJTF͸+4Ͱॻ͖΍͍͢Α͏ʹઃܭ͞Ε͍ͯΔ
    ‣ ෳࡶͳ৔߹ͩͱ݁ہ1SPNJTFΛωετͤͯ͞࢖͏ඞཁ͕͋Δ
    ‣ BTZODBXBJU͘Β͍͖ͬ͢ΓͱඇಉظͳίʔυΛॻ͖͍ͨ
    ʮ$MPKVSF4DSJQU͔Βͩͱ1SPNJTFΛѻ͏ίʔυ
    Λ͖ͬ͢Γॻ͚·ͤΜʯ ୅உੑձࣾһ

    ˞͜Ε͸ݸਓͷײ૝Ͱײ͡ํʹ͸ݸਓ͕ࠩ͋Γ·͢

    View Slide

  18. ར༻ऀ͔Βͷ͓೰Έ
    ‣ 1SPNJTFϔϏʔͳ"1*Λ࢖͏ͱ͖ʹ౎౓νϟωϧʹม׵ͨ͘͠ͳ͍
    ‣ HPϒϩοΫ͸े෼ʹෳࡶͳϚΫϩ
    ల։ܗ΋σΧ͍
    XFC"1*Λճୟͨ͘Ί͚ͩʹHPϒϩοΫΛॻ͖ͨ͘ͳ͍
    ‣ DPSFBTZOD͸Τϥʔॲཧ͕͏·͘ͳ͍
    ‣ TFMGIPTUFE$-+4 -VNP1MBODL
    Ͱ͸ͦͷ··Ͱ͸ಈ͔ͳ͍
    ʮDPSFBTZOD͸͔ͨ͠ʹڧྗͳಓ۩ͳΜ͚ͩͲɺ
    ͬ͘͠Γ͜ͳ͍৔߹΋͋Γ·͢ΑͶʯ ୅உੑձࣾһ

    ˞͜Ε͸ݸਓͷײ૝Ͱײ͡ํʹ͸ݸਓ͕ࠩ͋Γ·͢

    View Slide

  19. ސ٬͕ຊ౰ʹඞཁͩͬͨ΋ͷ
    ‣ ʮෳ਺ͷඇಉظॲཧΛஞ࣍తʹ࣮ߦ͢Δʯͱ͍͏໨తͷͨΊʹ
    ͸DPSFBTZOD͸ΦʔόʔΩϧ
    ‣ ඇಉظϓϩάϥϛϯάͷதఔ౓ʹෳࡶͳίʔυΛॻ͘ͷʹదͨ͠
    ಓ۩͕΄͍͠
    ໰୊ͷෳࡶ͞
    ίʔϧόοΫ
    Promise
    core.async

    View Slide

  20. ސ٬͕ຊ౰ʹඞཁͩͬͨ΋ͷ
    ‣ ʮෳ਺ͷඇಉظॲཧΛஞ࣍తʹ࣮ߦ͢Δʯͱ͍͏໨తͷͨΊʹ
    ͸DPSFBTZOD͸ΦʔόʔΩϧ
    ‣ ඇಉظϓϩάϥϛϯάͷதఔ౓ʹෳࡶͳίʔυΛॻ͘ͷʹదͨ͠
    ಓ۩͕΄͍͠
    ໰୊ͷෳࡶ͞
    ίʔϧόοΫ
    Promise
    core.async

    View Slide

  21. ސ٬͕ຊ౰ʹඞཁͩͬͨ΋ͷ
    ‣ ʮෳ਺ͷඇಉظॲཧΛஞ࣍తʹ࣮ߦ͢Δʯͱ͍͏໨తͷͨΊʹ
    ͸DPSFBTZOD͸ΦʔόʔΩϧ
    ‣ ඇಉظϓϩάϥϛϯάͷதఔ౓ʹෳࡶͳίʔυΛॻ͘ͷʹదͨ͠
    ಓ۩͕΄͍͠
    ‣ ႩఆόαϛͰ΋νΣʔϯιʔͰ΋ͳ͍מΓࠐΈόαϛ͕΄͍͠ʂ
    ໰୊ͷෳࡶ͞
    ίʔϧόοΫ
    Promise
    core.async

    View Slide

  22. LJUDIFOBTZOD

    View Slide

  23. LJUDIFOBTZOD
    ‣ 1SPNJTFΛத৺ͱͯ͠ɺ$MPKVSF4DSJQUͰඇಉظͳίʔυ
    Λॻ͖΍͘͢͢ΔϥΠϒϥϦ
    $MPKVSFͷΠσΟΦϜతͳॻ͖ํͰ1SPNJTFΛѻ͑Δߏจ
    DPSFBTZODͷνϟωϧ౳ͱγʔϜϨεʹ࿈ܞͰ͖Δ਌࿨ੑ
    ‣ ໊લ͸lFWFSZUIJOHCVUUIFLJUDIFOTJOLz ԿͰ΋͔ΜͰ΋

    Λ΋ͬͨ͡΋ͷ

    View Slide

  24. LJUDIFOBTZOD͕ఏڙ͢Δ"1*
    ‣ 1SPNJTF༝དྷͷؔ਺ɾϚΫϩ
    QSPNJTFɾUIFOɾDBUDI
    BMMɾSBDF
    ‣ $MPKVSFͷߏจͰ1SPNJTFΛѻ͏౶ҥߏจ
    QMFU
    εϨοσΟϯάϚΫϩ
    ϧʔϓ
    ྫ֎ॲཧ

    View Slide

  25. QSPNJTFɾUIFOɾDBUDI
    ‣ +4ͷ1SPNJTFͱ ΄΅
    ౳Ձͳ"1*Λఏڙ͍ͯ͠Δ
    (require ‘[kitchen-async.promise :as p])
    (-> (p/promise [resolve reject]
    (resolve 42))
    (p/then (fn [x]
    (js/console.log x)))
    (p/catch* (fn [err]
    (js/console.log err))))

    View Slide

  26. QMFUϚΫϩ
    (p/let [

    …]
    )
    (p/then
    (p/then (fn []
    (p/then
    (fn []
    … …)))))
    ্ͷϑΥʔϜ͸͓͓ΉͶҎԼͷΑ͏ʹల։͞ΕΔ

    View Slide

  27. QMFUϚΫϩ
    (p/let [browser (puppeteer/launch)
    page (.newPage browser)]
    (.goto page "https://www.example.com")
    (.screenshot page #js{:path "image.png"})
    (.close browser))
    (-> (puppeteer/launch)
    (.then (fn [browser]
    (-> (.newPage browser)
    (.then (fn [page]
    (.then (.goto page “https://www.example.com”)
    #(.screenshot page
    #js{:path "image.png"}))))
    (.then #(.close browser)))))))
    ্ͷίʔυ͸QMFUΛ࢖ͬͯҎԼͷΑ͏ʹॻ͖׵͑ΒΕΔ

    View Slide

  28. εϨοσΟϯάϚΫϩ
    (p/let [x (f)
    y (g x)]
    (h y))
    (p/-> (f) g h)
    ্ͷίʔυ͸QΛ࢖ͬͯҎԼͷΑ͏ʹॻ͖׵͑ΒΕΔ
    w Q QTPNF QTPNF΋ಉ༷ʹ࢖͑Δ

    View Slide

  29. ϧʔϓ
    ‣ QMPPQɾQSFDVSΛ࢖ͬͯ௨ৗͱಉ༷ʹϧʔϓ͕ॻ͚Δ
    ‣ QSFDVS͸QMPPQͷதͰͷΈ࢖༻Ͱ͖Δ
    (p/loop [i (p/timeout 1000 0)]
    (when (<= i 10)
    (prn i)
    (p/recur (p/timeout 1000 (inc i)))))

    View Slide

  30. ྫ֎ॲཧ
    ‣ QUSZɾQDBUDIɾQpOBMMZΛ࢖ͬͯ௨ৗͱಉ༷ʹྫ֎ॲཧ͕ॻ͚Δ
    ‣ QDBUDIɾQpOBMMZ͸QUSZͷதͰͷΈ࢖༻Ͱ͖Δ
    (p/try
    (write-async fd content)
    (p/catch :default e
    (js/console.log e))
    (p/finally
    (close fd)))

    View Slide

  31. DPSFBTZODͱͷ
    γʔϜϨεͳ࿈ܞ

    View Slide

  32. QSPNJTF
    ‣ ೚ҙͷ஋Λ1SPNJTFʹม׵ DPFSDJPO
    ͢Δؔ਺
    (defprotocol Promisable
    (->promise [this]))
    (extend-protocol Promisable
    js/Promise
    (->promise [this] this)
    default
    (->promise [this]
    (p/promise [resolve] (resolve this)))

    View Slide

  33. ҉໧ͷQSPNJTF
    ‣ LJUDIFOBTZODͷ͢΂ͯͷ"1*͸҉໧ʹQSPNJTFΛద༻
    ‣ 1SPNJTFҎ֎ͷ஋΋1SPNJTFͱ۠ผͳ͘࢖͏͜ͱ͕Ͱ͖Δ
    (p/let [x (f),
    y (g x)]
    (h y))
    (p/then p f) (p/then (->promise p) f)
    (p/let [x (->promise (f)),
    y (->promise (g x))]
    (h y))

    View Slide

  34. DPSFBTZODνϟωϧͱͷ࿈ܞ
    (ns kitchen-async.promise.from-channel
    (:require [clojure.core.async :as a]
    [clojure.core.async.impl.channels
    :refer [ManyToManyChannel]]
    [kitchen-async.promise :as p]))
    (extend-protocol p/Promisable
    ManyToManyChannel
    (->promise [ch]
    (p/promise [resolve reject]
    (a/go
    (let [x (a/(if (instance? js/Error x)
    (reject x)
    (resolve x)))))))

    View Slide

  35. DPSFBTZODνϟωϧͱͷ࿈ܞ
    ‣ dQSPNJTFGSPNDIBOOFMΛϩʔυ͢Ε͹νϟωϧͱ
    1SPNJTFΛγʔϜϨεʹ࢖͑ΔΑ͏ʹͳΔ
    (require ‘kitchen-async.promise.from-channel)
    (def ch (a/chan))
    (p/loop [i 0, x ch]
    (prn x)
    (when (<= i 10)
    (p/recur (inc i) ch)))

    View Slide

  36. ࣮༻ྫ
    ‣ DPSFBTZODͱಉ౳ͷ͜ͱ͕HPϚΫϩΛ࢖ΘͣʹͰ͖Δ
    ‣ ͨͩ͠ɺύϑΥʔϚϯεతʹ͸DPSFBTZODͷํ͕͍͍
    (defn debounce
    ([c ms] (debounce (a/chan) c ms))
    ([c' c ms]
    (p/loop [start nil loc c]
    (if (nil? start)
    (do
    (a/put! c' loc)
    (p/recur (js/Date.) nil))
    (p/let [loc c]
    (if (>= (- (js/Date.) start) ms)
    (p/recur nil loc)
    (p/recur (js/Date.) loc)))))
    c'))

    View Slide

  37. ·ͱΊ
    ‣ $MPKVSF$MPKVSF4DSJQUͰඇಉظϓϩάϥϛϯάͱ͍͏ͱ
    DPSFBTZOD͕༗໊͕ͩৗʹ࠷ྑͷબ୒ࢶͱ͸ݶΒͳ͍
    ‣ +BWB4DSJQU &$."4DSJQU
    ͸1SPNJTF΍BTZODBXBJU
    Λݴޠ࢓༷ʹऔΓೖΕ͍ͯͯɺ+4ͱͷJOUFSPQΛߟ͑Δͱ
    1SPNJTFΛʮ͏·͘ʯѻ͑Δ͜ͱ΋ඞཁ
    ‣ 1SPNJTFͱ΋DPSFBTZODͱ΋͏·͘΍͍ͬͯͨ͘Ίͷϥ
    ΠϒϥϦLJUDIFOBTZODΛ࡞ͬͨɺͱ͍͏࿩

    View Slide

  38. LJUDIFOBTZOD
    ‣ 1SPNJTFΛத৺ͱͯ͠ɺ$MPKVSF4DSJQUͰඇಉظͳίʔυ
    Λॻ͖΍͘͢͢ΔϥΠϒϥϦ
    ‣ $MPKVSFͷΠσΟΦϜతͳॻ͖ํͰ1SPNJTFΛѻ͑Δߏจ
    ‣ DPSFBTZODͷνϟωϧͱγʔϜϨεʹ࿈ܞͰ͖Δ਌࿨ੑ

    View Slide