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

新しいプログラミング言語の学び方 HTTPサーバーを作って学ぶ Java, Scala, Clojure

新しいプログラミング言語の学び方 HTTPサーバーを作って学ぶ Java, Scala, Clojure

JJUG CCC 2017 Fallでの発表資料です。

Shunsuke Tadokoro

November 24, 2017
Tweet

More Decks by Shunsuke Tadokoro

Other Decks in Technology

Transcript

  1. DDD@H ͓࿩͢͠Δ͜ͱ w ͳͥ)551αʔόʔ  w 4DBMB $MPKVSFʹ͍ͭͯ w )551αʔόʔΛ࡞Γͳ͕Βֶ΅͏

    w ͬ͘͟ΓΞʔΩςΫνϟ w ֤ݴޠͰ࣮૷ͯ͠ΈΔ w 4PDLFUͷѻ͍ w ਖ਼نදݱ w Ϧιʔεͷ։์ w ฒྻॲཧ w จࣈྻͷѻ͍ w ·ͱΊ
  2. DDD@H 4DBMB w +BWBͱͷ૬ޓӡ༻ੑ w γʔϜϨεͳݺͼग़͠ɺ+BWBඪ४ϥΠϒϥϦͷ࠶ར༻ w ؆ܿੑ w লུՄೳͳߏจɺܕਪ࿦ɺڧྗͳඪ४ϥΠϒϥϦ

    w ந৅౓ͷߴ͍ίʔυɺ৽੍͍͠ޚߏจΛఆٛͰ͖Δදݱྗ w 8IBUͷڧௐɺ)PXͷӅณ w ੩తܕ෇͚ w ݕূՄೳੑɺϦϑΝΫλͷ͠΍͢͞ɺυΩϡϝϯτੑ
  3. DDD@H $MPKVSF w -JTQ w จ๏͕গͳ͍ɺσʔλͱͯ͠ͷίʔυ w ؔ਺ܕϓϩάϥϛϯάͷͨΊͷݴޠ w ୈҰڃؔ਺ɺΠϛϡʔλϒϧͳσʔλߏ଄ɺ࠶ؼతͳϧʔϓ

    w +BWBͱͷ૬ޓӡ༻ੑ w ฒߦॲཧͷͨΊʹઃܭ w 3&1-Λ׆͔ͨ͠ΠϯλϥΫςΟϒΠϯΫϦϝϯλϧͳ ։ൃ
  4. DDD@H ࢛ଇԋࢉ 12 + 40 10 - 1 2 *

    3 5 / 2 +BWB 4DBMB (+ 12 40) (- 10 1) (* 2 3) (/ 5 2) ; -> 5/2 ෼਺Λѻ͏Ratioܕ $MPKVSF
  5. DDD@H ม਺એݴ int x = 10; +BWB val x =

    10 val x: Int = 10 4DBMB (def x 10) (let [y 10] (+ y 3)) ; y͸letͷׅހ಺͚ͩͰࢀরͰ͖Δ $MPKVSF
  6. DDD@H ϝιουɾؔ਺એݴ public int f(int x) { return x +

    1; } +BWB (defn f [x] (+ x 1)) $MPKVSF def f(x: Int) = x + 1 4DBMB
  7. DDD@H ࠓճ༻ҙͨ͠)551αʔόʔ w ىಈ͠ɺMPDBMIPTUͷಛఆͷϙʔτͰ)551ϦΫΤετΛ଴ͪड͚Δ w ରԠ͢Δ)551ϦΫΤετϝιου͸(&5ͷΈ
 ͦΕҎ֎ͷϝιου΋(&5ͱΈͳ͢ w QVCMJDσΟϨΫτϦΑΓ্ͷ֊૚΁ͷϦΫΤετʹ͸
 'PSCJEEFOΛฦ͢

    w Ϧιʔεͷ.*.&͸֎෦ϑΝΠϧͰઃఆͰ͖Δ w ϦΫΤετΛϒϩοΫ͠ͳ͍ʢϚϧνεϨουʣ w ,FFQ"MJWF͸͠ͳ͍ɻίωΫγϣϯ͸౎౓DMPTF͢Δ w )551$BDIF͸͠ͳ͍ɻ͸ฦ͞ͳ͍
  8. DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java

    ├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java ΞϓϦέʔγϣϯͷΤϯτϦʔϙΠϯτ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF
  9. DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java

    ├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java )551ϦΫΤετΛύʔε͢Δ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF
  10. DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java

    ├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java ϦΫΤετΛද͢ΦϒδΣΫτ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF
  11. DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java

    ├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java ϦΫΤετ͔ΒϨεϙϯεΛੜ੒ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF
  12. DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java

    ├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java ϨεϙϯεΛද͢ΦϒδΣΫτ 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF
  13. DDD@H ͭ͘Γʹ͍ͭͯͬ͘͟Γͱ ├── MimeDetector.java ├── Request.java ├── RequestHandler.java ├── RequestParser.java

    ├── Response.java ├── SimpleJavaHttpServer.java └── WorkerThread.java )551ϨεϙϯεΛ8SJUF 3FRVFTU *OQVU4USFBN 3FTQPOTF 0VUQVU4USFBN ϦΫΤετͷύʔε ϦΫΤετͷϋϯυϦϯά Ϩεϙϯεͷ8SJUF
  14. DDD@H 4PDLFUͷѻ͍ // αʔόʔιέοτͷੜ੒ ServerSocket serverSocket = new ServerSocket(8080); while

    (true) { // ઀ଓΛ଴ͪड͚Δɻ઀ଓ͞ΕΔ·ͰϒϩοΫɻ Socket socket = serverSocket.accept(); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); ... } 4JNQMF+BWB)UUQ4FSWFSKBWBʢൈਮʣ
  15. DDD@H 4PDLFUͷѻ͍ // αʔόʔιέοτͷੜ੒ val serverSocket = new ServerSocket(8080) while

    (true) { // ઀ଓΛ଴ͪड͚Δɻ઀ଓ͞ΕΔ·ͰϒϩοΫ val socket = serverSocket.accept val in = s.getInputStream val out = s.getOutputStream ... } 4JNQMF)UUQ4FSWFSTDBMBʢൈਮʣ
  16. DDD@H 4PDLFUͷѻ͍ (let [server-socket (new ServerSocket 8080)] (while true (let

    [socket (.accept server-socket) in (.getInputStream socket) out (.getOutputStream socket)] ...))) DPSFDMKʢൈਮʣ
  17. DDD@H ໊લ෇͖άϧʔϓ w ਖ਼نදݱͷάϧʔϓʹ໊લΛ෇͚Δ͜ͱ͕Ͱ͖Δ w άϧʔϓͷॱং͕มΘͬͨͱͯ͠΋औΓग़͢ॲཧ͸ͦͷ·· import java.util.regex.*; String regex

    = "(?<year>\\d+)/(?<month>\\d+)/(?<day>\\d+)"; Pattern p = Pattern.compile(regex); Matcher m = p.matcher("2017/11/18"); if (m.find()) { System.out.println(m.group("year")); // 2017 System.out.println(m.group("month")); // 11 System.out.println(m.group("day")); // 18 }
  18. DDD@H w ϦΫΤετϥΠϯͷ֤ཁૉʹ໊લΛ෇͚ͯநग़ public static Pattern requestLinePattern = Pattern.compile("(?<method>.*) (?<path>.*?)

    (?<version>.*?)"); public Request fromInputStream(InputStream in){ BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String requestLine = reader.readLine(); Matcher matcher = requestLinePattern.matcher(requestLine); if (!matcher.find()) return null; String method = matcher.group("method"); String targetPath = matcher.group("path"); String httpVersion = matcher.group("version"); return new Request(method, targetPath, httpVersion); } ໊લ෇͖άϧʔϓ 3FRVFTU1BSTFSKBWBʢൈਮʣ
  19. DDD@H ύλʔϯϚον w +BWBͷTXJUDIจʹࣅ͍ͯΔ͕ɺΑΓॊೈͰڧྗ w ஋ʹҰக͢Δ͔͚ͩͰͳ͘ɺܕ΍ߏ଄Ͱ΋෼ذ 0 match { case

    0 => "Zero" // "Zero" case _ => "Other" } List(1, 2, 3) match { case List(_, x, _) => x // 2 case _ => -1 } 1 match { x: Int => s"$x͸Int" // 1͸Int x: String => s"$x͸String" x => s"$x͸???" }
  20. DDD@H ύλʔϯϚον val pattern = "(.+) (.+) (.+)".r requestLine match

    { case pattern(method, path, version) => Some(Request(method, path, version)) case _ => None } 3FRVFTU1BSTFSTDBMBʢൈਮʣ
  21. DDD@H 0QUJPOܕ requestLine match { case pattern(method, path, version) =>

    Some(Request(method, path, version)) case _ => None } w +BWBͰ͍͏0QUJPOBM w 4PNFͱ/POF͔ΒͳΔܕ w ஋͕͋Δ͔ͳ͍͔෼͔Βͳ͍ঢ়ଶΛද͢ w ஋͕ଘࡏ͢Δ͔ͷνΣοΫΛڧ੍Ͱ͖Δ
  22. DDD@H SFpOE w ਖ਼نදݱʹϚονͨ͠จࣈྻΛऔಘ w Ҿ਺ʹάϧʔϓԽͨ͠ਖ਼نදݱΦϒδΣΫτΛ౉͢ͱɺ
 ઌ಄ʹ͸Ϛονͨ͠จࣈྻશମɺҎ߱ʹΩϟϓνϟ͞ΕͨจࣈྻͷϕΫλʔ ;; #"" ͸java.util.regex.PatternͷϦςϥϧ

    (re-find #"(.+)/(.+)/(.+)" "2017/11/18") ;; => ["2017/11/18" "2017" "11" "18"] ;; restͰઌ಄Ҏ֎ͷཁૉΛऔಘ (rest (re-find #"(.+)/(.+)/(.+)" "2017/11/18")) ;; => ("2017" "11" "18")
  23. DDD@H SFpOE [JQNBQ (let [line (.readLine reader)] (zipmap [:method :path

    :version] (rest (re-find #"(.+) (.+) (.+)" line)))) ;; => {:method "GET", :path "/", :version "HTTP/1.1"} SFRVFTU@QBSTFSDMKʢൈਮʣ
  24. DDD@H SFpOE [JQNBQ (let [line (.readLine reader)] (zipmap [:method :path

    :version] (rest (re-find #"(.+) (.+) (.+)" line)))) ;; => {:method "GET", :path "/", :version "HTTP/1.1"} SFRVFTU@QBSTFSDMKʢൈਮʣ <NFUIPEQBUIWFSTJPO> <(&5)551>
  25. DDD@H SFpOE [JQNBQ (let [line (.readLine reader)] (zipmap [:method :path

    :version] (rest (re-find #"(.+) (.+) (.+)" line)))) ;; => {:method "GET", :path "/", :version "HTTP/1.1"} SFRVFTU@QBSTFSDMKʢൈਮʣ <NFUIPEQBUIWFSTJPO> <(&5)551>
  26. DDD@H 5SZXJUI3FTPVSDFT try (InputStream in = new FileInputStream(file)) { //

    Կ͔ϦιʔεΛѻ͏ॲཧ } // try۟Λൈ͚ͨΒclose w USZ۟Λൈ͚ͨΒࣗಈͰDMPTF w ର৅͸KBWBJP$MPTFBCMF 
 KBWBMBOH"VUP$MPTFBCMFͷ࣮૷Ϋϥε
  27. DDD@H -PBO1BUUFSO w ʮआΓͨΒฦ͢ʯΛ࣮֬ʹߦ͏ w ੍ޚߏ଄ͷΑ͏ʹݟ͔͚࣮ͤͯ͸ϝιου val reader = new

    BufferedReader(...) using(reader) { r => // readerΛ࢖ͬͨԿ͔ͷॲཧ } // ϒϩοΫΛൈ͚ͨΒreader͸close͞Ε͍ͯΔ
  28. DDD@H -PBO1BUUFSOͷ࣮૷ def using[A, R <: Closeable](resource: R)(f: R =>

    A): A = { try { f(resource) } finally { resource.close() } }
  29. DDD@H -PBO1BUUFSOͷ࣮૷ def using[A, R <: Closeable](resource: R)(f: R =>

    A): A = { try { f(resource) } finally { resource.close() } } ܕม਺3͸$MPTFBCMF
  30. DDD@H -PBO1BUUFSOͷ࣮૷ def using[A, R <: Closeable](resource: R)(f: R =>

    A): A = { try { f(resource) } finally { resource.close() } } 3ܕͷԿ͔Λड͚औΓɺԿΒ͔ͷܕ"Λฦؔ͢਺ΛҾ਺ʹͱΔ
  31. DDD@H -PBO1BUUFSOͷ࣮૷ def using[A, R <: Closeable](resource: R)(f: R =>

    A): A = { try { f(resource) } finally { resource.close() } } Ϧιʔεʹରͯؔ͠਺Λద༻
  32. DDD@H -PBO1BUUFSOͷ࣮૷ def using[A, R <: Closeable](resource: R)(f: R =>

    A): A = { try { f(resource) } finally { resource.close() } } ࠷ޙʹDMPTF
  33. DDD@H ׅހ ͱதׅހ\^ "hoge".startsWith{"ho"} // ͋·Γ΍Βͳ͍ "hoge".replace{"ho", "fu"} // Ͱ͖ͳ͍

    "hoge" map { c => c.someFunc ... } // Α͘΍Δ w Ҿ਺͕Ұ͚ͭͩͷ৔߹ɺׅހΛதׅހͰॻ͍ͯ΋ྑ͍ w Ҿ਺ʹؔ਺Λ౉͢৔߹ɺதׅހΛ࢖͏͜ͱ͕Α͋͘Δ
  34. DDD@H -PBO1BUUFSO using(socket) { s => val in = s.getInputStream

    val out = s.getOutputStream val request = parser.fromInputStream(in) val response = request.map(handleRequest) response.foreach(_.writeTo(out)) } 4JNQMF)UUQ4FSWFSTDBMBʢൈਮʣ
  35. DDD@H 5ISFBE class HogeThread extends Thread { public void run()

    { // Կ͔ඇಉظʹ࣮ߦ͍ͨ͠ॲཧ } } HogeThread h = new HogeThread(); h.start(); class FugaRunnable implements Runnable { public void run() { // Կ͔ඇಉظʹ࣮ߦ͍ͨ͠ॲཧ } } FugaRunnable f = new Thread(new FugaRunnable()); f.start(); 5ISFBEΛFYUFOET3VOOBCMFΛJNQMFNFOUT
  36. DDD@H 5ISFBE public class WorkerThread extends Thread { private Socket

    socket; private RequestParser parser; private RequestHandler handler; public WorkerThread( Socket socket, RequestParser parser, RequestHandler handler) { ... 8PSLFS5ISFBEKBWBʢൈਮʣ 4JNQMF+BWB)UUQ4FSWFSKBWBʢൈਮʣ Thread worker = new WorkerThread(socket, parser, handler); worker.start();
  37. DDD@H &YFDVUPS4FSWJDF ExecutorService cachedPool = Executors.newCachedThreadPool(); cachedPool.execute(runnable); ExecutorService fixedPool =

    Executors.newFixedThreadPool(4); fixedPool.execute(runnable); w ʮλεΫʯΛผεϨουͰॲཧ͢ΔͨΊͷ࢓૊Έ w εϨουϓʔϧͷछྨΛࢦఆͰ͖Δ
  38. DDD@H 'VUVSF w 'VUVSFBQQMZ͸౉͞ΕͨॲཧΛඇಉظʹ࣮ߦ w ͍ͭͲͷΑ͏ʹඇಉظʹ࣮ߦ͢Δ͔͸&YFDVUJPO$POUFYU࣍ୈ import scala.concurrent.Future // ForkJoinPoolɺσϑΥϧτͰ͸ίΞ਺෼ͷฒྻ౓Ͱॲཧ

    import scala.concurrent.ExecutionContext. Implicits.global val result: Future[User] = Future { userRepository.fetch(userId) } result.map(user => user.id)
  39. DDD@H &YFDVUJPO$POUFYU͸Ͳ͏ड͚औΔʁ w 'VUVSFBQQMZʹ͸Ҿ਺ϒϩοΫ͕ͭ w ͭ໨ͷҾ਺ϒϩοΫͰ&YFDVUJPO$POUFYUΛड͚औΔ object Future { ...

    def apply[T](body: =>T)(implicit executor: ExecutionContext) } Future { userRepository.fetch(user) } Future.apply( userRepository.fetch(user) )
  40. DDD@H JNQMJDJU8IBUͱ)PXͷ෼཭ w )PX͕ڊେʹͳΔͱɺίʔυͷຊདྷͷҙਤ͕ຒ΋Εͯ͠·͏ w JNQMJDJUͷେ͖ͳϞνϕʔγϣϯ͸)PXͷӅณ w 8IBUͱ)PXΛ෼཭͢Δ͜ͱͰɺ໨తΛ୺తʹࣔ͢͜ͱ͕Ͱ͖Δ val values:

    Seq[(String, Option[Int])] val sorted = sort(values)( tuple2Comparator( stringComparator, optionComparator(intComparator))) val sorted = sort(values) // implicit
  41. DDD@H $MPKVSFͷฒߦॲཧؔ਺ εϨουϓʔϧ εϨου਺ TFOE 'JYFE5ISFBE1PPM  ίΞ਺ TFOEPGG $BDIFE5ISFBE1PPM

    ੍ݶͳ͠ GVUVSFGVUVSFDBMM QNBQQDBMMT $BDIFE5ISFBE1PPM ੍ݶͳ͠ HP 'JYFE5ISFBE1PPM ίΞ਺   UISFBE
 UISFBEDBMM $BDIFE5ISFBE1PPM ੍ݶͳ͠ SFEVDFST 'PSL+PJO1PPM ࣗಈ੍ޚ IUUQUZBOPTIFMpODDPNQPTUDMPKVSFDPODVSSFOU
  42. DDD@H $MPKVSFͷฒߦॲཧؔ਺ εϨουϓʔϧ εϨου਺ TFOE 'JYFE5ISFBE1PPM  ίΞ਺ TFOEPGG $BDIFE5ISFBE1PPM

    ੍ݶͳ͠ GVUVSFGVUVSFDBMM QNBQQDBMMT $BDIFE5ISFBE1PPM ੍ݶͳ͠ HP 'JYFE5ISFBE1PPM ίΞ਺   UISFBE
 UISFBEDBMM $BDIFE5ISFBE1PPM ੍ݶͳ͠ SFEVDFST 'PSL+PJO1PPM ࣗಈ੍ޚ IUUQUZBOPTIFMpODDPNQPTUDMPKVSFDPODVSSFOU
  43. DDD@H $MPKVSFͷฒߦॲཧؔ਺ εϨουϓʔϧ εϨου਺ TFOE 'JYFE5ISFBE1PPM  ίΞ਺ TFOEPGG $BDIFE5ISFBE1PPM

    ੍ݶͳ͠ GVUVSFGVUVSFDBMM QNBQQDBMMT $BDIFE5ISFBE1PPM ੍ݶͳ͠ HP 'JYFE5ISFBE1PPM ίΞ਺   UISFBE
 UISFBEDBMM $BDIFE5ISFBE1PPM ੍ݶͳ͠ SFEVDFST 'PSL+PJO1PPM ࣗಈ੍ޚ IUUQUZBOPTIFMpODDPNQPTUDMPKVSFDPODVSSFOU
  44. DDD@H UISFBE (thread (with-open [s socket in (.getInputStream s) out

    (.getOutputStream s)] (-> (request-parser/from-input-stream in) (request-handler/handle-request) (response-writer/write out)))) DPSFDMKʢൈਮʣ
  45. DDD@H จࣈྻ݁߹ w ϨεϙϯεʢΦϒδΣΫτϚοϓʣ͔Β
 )551ϨεϙϯεϔομΛ૊Έཱ͍ͯͨ public class Response { public

    final Status status; public final String contentType; public final int contentLength; public final byte[] body; }
  46. DDD@H 4USJOH#VJMEFS String response = "HTTP/1.1 " + status.statusCode +

    CRLF + "Server: SimpleJavaHttpServer" + CRLF + "Content-Type: " + contentType + CRLF + "Content-Length: " + 
 String.valueOf(contentLength) + CRLF + "Connection: Close" + CRLF + CRLF; 3FTQPOTFKBWBʢൈਮʣ ࠷దԽ͞ΕΔ͔ࣗ৴͕ͳ͚Ε͹KBWBQD
  47. DDD@H 5SJQMF2VPUF "Hello triple quote!\nHello stripMargin!" """Hello triple quote! Hello

    stripMargin!""" """|Hello triple quote! |Hello stripMargin!""".stripMargin w վߦΛؚΉจࣈྻΛຒΊࠐΉʹ͸ɺΛ࢖͏ w ΠϯσϯτΛଗ͑Δʹ͸Πϯσϯτจࣈ c ͱTUSJQ.BSHJOΛ࢖͏
  48. DDD@H 4USJOH*OUFSQPMBUJPOͱ5SJQMF2VPUF val response = s"""HTTP/1.1 ${status.value} |Date: ${rfc1123Formatter.format(now)} |Server:

    SimpleScalaHttpServer |Content-Type: $contentType |Content-Length: ${body.length.toString} |Connection: Close | |""".stripMargin 3FTQPOTFTDBMBʢൈਮʣ
  49. DDD@H w $MPKVSFͰ͸จࣈྻͷ݁߹͸ Ͱ͸ͳ͘TUS w Մม௕Ҿ਺ɺ4ࣜͳΒͰ͸ TUS (+ "hoge" "fuga")

    ClassCastException java.lang.String cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:128) (str "hoge" "fuga" "piyo") // => hogefugapiyo
  50. DDD@H TUS (let [header (str "HTTP/1.1" sp status sp reason-phrase

    crlf "Content-Length:" (count body) crlf "Content-Type:" content-type crlf "Connection: Close" crlf crlf)] ...) SFTQPOTF@XSJUFSDMKʢൈਮʣ