2018/01/29のLisp meetup #60の発表資料です。
LJUDIFOBTZODBQSPNJTJOH 1SPNJTFMJCSBSZPSBQPPSNBO`TDPSFBTZOD-JTQNFFUVQ!BUIPT
View Slide
ࣗݾհ‣ 5XJUUFS!BUIPT‣ (JU)VCBUIPT‣ $MPKVSF 4DSJQUॻ͍ͯ·͢ 3FBDU/BUJWFΞϓϦΛ$MPKVSF4DSJQUͰ "MFYB4LJMMΛ$MPKVSF4DSJQUͰ
5-%3‣ $MPKVSF$MPKVSF4DSJQUͰඇಉظϓϩάϥϛϯάͱ͍͏ͱDPSFBTZOD͕༗໊͕ͩৗʹ࠷ྑͷબࢶͱݶΒͳ͍‣ +BWB4DSJQU &$."4DSJQU1SPNJTFBTZODBXBJUΛݴޠ༷ʹऔΓೖΕ͍ͯͯɺ+4ͱͷJOUFSPQΛߟ͑Δͱ1SPNJTFΛʮ͏·͘ʯѻ͑Δ͜ͱඞཁ‣ 1SPNJTFͱDPSFBTZODͱ͏·͍ͬͯͨ͘͘ΊͷϥΠϒϥϦLJUDIFOBTZODΛ࡞ͬͨɺͱ͍͏
ࠓ͢͜ͱ‣ എܠ +BWB4DSJQUք۾ͷඇಉظϓϩάϥϛϯάࣄ $MPKVSF4DSJQUք۾ͷඇಉظϓϩάϥϛϯάࣄ‣ $MPKVSF4DSJQUͰͷඇಉظϓϩάϥϯάͷ‣ LJUDIFOBTZODͷհ
എܠ
+BWB4DSJQUք۾ͷඇಉظࣄ‣ +BWB4DSJQUͷଟ͘ͷॲཧܥγϯάϧεϨου‣ *0ॲཧͷྃΛಉظతʹͪ߹Θͤͯ͠·͏ͱɺͦͷεϨουॲཧྃ·ͰԿͰ͖ͳ͘ͳΔ‣ *0ͷྃͪͷؒεϨουΛଞͷॲཧʹ໌͚͢͜ͱͰεϨουΛޮతʹ͑ΔΑ͏ʹͳΔˠඇಉظϓϩάϥϛϯά
ίʔϧόοΫ‣ ॲཧ͕ྃͨ͠ޙʹ࣮ߦ͢ΔॲཧΛ͢‣ +BWB4DSJQUͱͯͬ͠ͱݪ࢝తͳඇಉظ"1*ͷ࡞Γํ‣ ඇಉظॲཧ͕ଟஈʹͳΔͱίʔϧόοΫ͕ωετͯ͠ίʔυͷՄಡੑ͕ѱ͘ͳΔ ͍ΘΏΔʮίʔϧόοΫࠈʯconst fs = require(‘fs’)fs.readFile(‘input.txt’, (err, data) => {console.log(data)})
1SPNJTF‣ 1SPNJTFඇಉظͳܭࢉ݁ՌΛอ࣋͢Δσʔλܕ‣ ίʔϧόοΫΛҾʹड͚ΔΘΓʹ1SPNJTFΛฦ͢Α͏ʹ͓ͯ͘͠ͱʜconst readFileAsync = (path) => {return new Promise((resolve, reject) => {fs.readFile(path, (err, data) => {if (err) return reject(err)resolve(data)})})}
1SPNJTF‣ ॲཧͷྃޙʹ࣮ߦ͢ΔॲཧΛޙ͔ΒઃఆͰ͖Δ‣ ϝιουνΣʔϯͰଟஈͷॲཧΛܨ͛ΒΕΔreadFileAsync(‘input.txt’).then((text) => {return JSON.parse(text)}).then((json) => {console.log(json)}).catch((err) => {console.log(err)})
BTZODBXBJU‣ 1SPNJTFΛͬͨॲཧΛܗࣜͰ͔͋ͨಉظతͳॲཧͷΑ͏ʹهड़Ͱ͖Δ‣ ଟ͘ͷ߹1SPNJTFΛ͏ΑΓ੍ޚϑϩʔ͕͔Γ͘͢ͳΔ(async () => {try {const text =await readFileAsync(‘input.txt’)return JSON.parse(text)} catch (err) {console.log(err)}})()
+BWB4DSJQUք۾ͷඇಉظࣄ‣ &$."4DSJQU &4ͷ༷ͷऔΓࠐΈ &41SPNJTF &4BTZODBXBJU‣ 8FCඪ४ͷ"1*Ͱͷར༻ 'FUDI"1* 8FC35$ͷϝσΟΞऔಘ 4FSWJDF8PSLFS FUDFUD‣ αʔυύʔςΟͷOQNϞδϡʔϧͰͷར༻+BWB4DSJQUͷ༷ʑͳ"1*ʹ1SPNJTF͕ΘΕͭͭ͋Δ
$MPKVSF4DSJQUք۾ͷඇಉظࣄ‣ ΄΅DPSFBTZODҰͳงғؾ‣ DPSFBTZOD$41 ڠௐεϨουͱνϟωϧΛհͨ͠ϝοηʔδύογϯάΛϞσϧʹ͍ͯ͠Δ‣ 1SPNJTFͱಉ༷ͷ͍ํͰ͖Δ͕ɺͭͷνϟωϧ͔ΒෳճΛड͚औͬͨΓɺΛૹΓฦͤΔΑΓڧྗ
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)))
ར༻ऀ͔Βͷ͓Έʮ$MPKVSF4DSJQU͔Βͩͱ1SPNJTFΛѻ͏ίʔυΛ͖ͬ͢Γॻ͚·ͤΜʯ உੑձࣾһ˞͜ΕݸਓͷײͰײ͡ํʹݸਓ͕ࠩ͋Γ·͢
ར༻ऀ͔Βͷ͓Έʮ$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))))˞͜ΕݸਓͷײͰײ͡ํʹݸਓ͕ࠩ͋Γ·͢
ར༻ऀ͔Βͷ͓Έʮ$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)))))))˞͜ΕݸਓͷײͰײ͡ํʹݸਓ͕ࠩ͋Γ·͢
ར༻ऀ͔Βͷ͓Έ‣ ͦͦ1SPNJTF+4Ͱॻ͖͍͢Α͏ʹઃܭ͞Ε͍ͯΔ‣ ෳࡶͳ߹ͩͱ݁ہ1SPNJTFΛωετͤͯ͞͏ඞཁ͕͋Δ‣ BTZODBXBJU͘Β͍͖ͬ͢ΓͱඇಉظͳίʔυΛॻ͖͍ͨʮ$MPKVSF4DSJQU͔Βͩͱ1SPNJTFΛѻ͏ίʔυΛ͖ͬ͢Γॻ͚·ͤΜʯ உੑձࣾһ˞͜ΕݸਓͷײͰײ͡ํʹݸਓ͕ࠩ͋Γ·͢
ར༻ऀ͔Βͷ͓Έ‣ 1SPNJTFϔϏʔͳ"1*Λ͏ͱ͖ʹνϟωϧʹมͨ͘͠ͳ͍‣ HPϒϩοΫेʹෳࡶͳϚΫϩ ల։ܗσΧ͍ XFC"1*Λճୟͨ͘Ί͚ͩʹHPϒϩοΫΛॻ͖ͨ͘ͳ͍‣ DPSFBTZODΤϥʔॲཧ͕͏·͘ͳ͍‣ TFMGIPTUFE$-+4 -VNP1MBODLͰͦͷ··Ͱಈ͔ͳ͍ʮDPSFBTZOD͔ͨ͠ʹڧྗͳಓ۩ͳΜ͚ͩͲɺͬ͘͠Γ͜ͳ͍߹͋Γ·͢ΑͶʯ உੑձࣾһ˞͜ΕݸਓͷײͰײ͡ํʹݸਓ͕ࠩ͋Γ·͢
ސ٬͕ຊʹඞཁͩͬͨͷ‣ ʮෳͷඇಉظॲཧΛஞ࣍తʹ࣮ߦ͢Δʯͱ͍͏తͷͨΊʹDPSFBTZODΦʔόʔΩϧ‣ ඇಉظϓϩάϥϛϯάͷதఔʹෳࡶͳίʔυΛॻ͘ͷʹదͨ͠ಓ۩͕΄͍͠ͷෳࡶ͞ίʔϧόοΫPromisecore.async
ސ٬͕ຊʹඞཁͩͬͨͷ‣ ʮෳͷඇಉظॲཧΛஞ࣍తʹ࣮ߦ͢Δʯͱ͍͏తͷͨΊʹDPSFBTZODΦʔόʔΩϧ‣ ඇಉظϓϩάϥϛϯάͷதఔʹෳࡶͳίʔυΛॻ͘ͷʹదͨ͠ಓ۩͕΄͍͠‣ ႩఆόαϛͰνΣʔϯιʔͰͳ͍מΓࠐΈόαϛ͕΄͍͠ʂͷෳࡶ͞ίʔϧόοΫPromisecore.async
LJUDIFOBTZOD
LJUDIFOBTZOD‣ 1SPNJTFΛத৺ͱͯ͠ɺ$MPKVSF4DSJQUͰඇಉظͳίʔυΛॻ͖͘͢͢ΔϥΠϒϥϦ $MPKVSFͷΠσΟΦϜతͳॻ͖ํͰ1SPNJTFΛѻ͑Δߏจ DPSFBTZODͷνϟωϧͱγʔϜϨεʹ࿈ܞͰ͖Δੑ‣ ໊લlFWFSZUIJOHCVUUIFLJUDIFOTJOLz ԿͰ͔ΜͰΛͬͨ͡ͷ
LJUDIFOBTZOD͕ఏڙ͢Δ"1*‣ 1SPNJTF༝དྷͷؔɾϚΫϩ QSPNJTFɾUIFOɾDBUDI BMMɾSBDF‣ $MPKVSFͷߏจͰ1SPNJTFΛѻ͏ҥߏจ QMFU εϨοσΟϯάϚΫϩ ϧʔϓ ྫ֎ॲཧ
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))))
QMFUϚΫϩ(p/let [ …])(p/then (p/then (fn [](p/then (fn []… …)))))্ͷϑΥʔϜ͓͓ΉͶҎԼͷΑ͏ʹల։͞ΕΔ
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ΛͬͯҎԼͷΑ͏ʹॻ͖͑ΒΕΔ
εϨοσΟϯάϚΫϩ(p/let [x (f)y (g x)](h y))(p/-> (f) g h)্ͷίʔυQΛͬͯҎԼͷΑ͏ʹॻ͖͑ΒΕΔw Q QTPNF QTPNFಉ༷ʹ͑Δ
ϧʔϓ‣ QMPPQɾQSFDVSΛͬͯ௨ৗͱಉ༷ʹϧʔϓ͕ॻ͚Δ‣ QSFDVSQMPPQͷதͰͷΈ༻Ͱ͖Δ(p/loop [i (p/timeout 1000 0)](when (<= i 10)(prn i)(p/recur (p/timeout 1000 (inc i)))))
ྫ֎ॲཧ‣ QUSZɾQDBUDIɾQpOBMMZΛͬͯ௨ৗͱಉ༷ʹྫ֎ॲཧ͕ॻ͚Δ‣ QDBUDIɾQpOBMMZQUSZͷதͰͷΈ༻Ͱ͖Δ(p/try(write-async fd content)(p/catch :default e(js/console.log e))(p/finally(close fd)))
DPSFBTZODͱͷγʔϜϨεͳ࿈ܞ
QSPNJTF‣ ҙͷΛ1SPNJTFʹม DPFSDJPO͢Δؔ(defprotocol Promisable(->promise [this]))(extend-protocol Promisablejs/Promise(->promise [this] this)default(->promise [this](p/promise [resolve] (resolve this)))
҉ͷ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))
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/PromisableManyToManyChannel(->promise [ch](p/promise [resolve reject](a/go(let [x (a/(if (instance? js/Error x)(reject x)(resolve x)))))))
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)))
࣮༻ྫ‣ 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'))
·ͱΊ‣ $MPKVSF$MPKVSF4DSJQUͰඇಉظϓϩάϥϛϯάͱ͍͏ͱDPSFBTZOD͕༗໊͕ͩৗʹ࠷ྑͷબࢶͱݶΒͳ͍‣ +BWB4DSJQU &$."4DSJQU1SPNJTFBTZODBXBJUΛݴޠ༷ʹऔΓೖΕ͍ͯͯɺ+4ͱͷJOUFSPQΛߟ͑Δͱ1SPNJTFΛʮ͏·͘ʯѻ͑Δ͜ͱඞཁ‣ 1SPNJTFͱDPSFBTZODͱ͏·͍ͬͯͨ͘͘ΊͷϥΠϒϥϦLJUDIFOBTZODΛ࡞ͬͨɺͱ͍͏
LJUDIFOBTZOD‣ 1SPNJTFΛத৺ͱͯ͠ɺ$MPKVSF4DSJQUͰඇಉظͳίʔυΛॻ͖͘͢͢ΔϥΠϒϥϦ‣ $MPKVSFͷΠσΟΦϜతͳॻ͖ํͰ1SPNJTFΛѻ͑Δߏจ‣ DPSFBTZODͷνϟωϧͱγʔϜϨεʹ࿈ܞͰ͖Δੑ