0
|
1 <!DOCTYPE html>
|
|
2 <!--
|
|
3 Google HTML5 slide template
|
|
4
|
|
5 Authors: Luke Mahé (code)
|
|
6 Marcin Wichary (code and design)
|
|
7
|
|
8 Dominic Mazzoni (browser compatibility)
|
|
9 Charles Chen (ChromeVox support)
|
|
10
|
|
11 URL: http://code.google.com/p/html5slides/
|
|
12 -->
|
|
13
|
|
14 <html>
|
|
15 <head>
|
|
16 <title>Haskell による Web Service 構築入門</title>
|
|
17
|
|
18 <meta charset='utf-8'>
|
|
19 <script
|
|
20 src='http://web.amothic.com/html5slides/slides.js'></script>
|
|
21 </head>
|
|
22
|
|
23 <style>
|
|
24 /* Your individual styles here, or just use inline styles if that’s
|
|
25 what you want. */
|
|
26
|
|
27
|
|
28 </style>
|
|
29
|
|
30 <body style='display: none'>
|
|
31
|
|
32 <section class='slides layout-regular template-concurrency'>
|
|
33
|
|
34 <article>
|
|
35 <h1>
|
|
36 Haskell による Web Service 構築入門
|
|
37 </h1>
|
|
38 <p>
|
|
39 Daichi TOMA
|
|
40 <br>
|
|
41 Jul 6, 2013
|
|
42 </p>
|
|
43 </article>
|
|
44
|
|
45 <article>
|
|
46 <h3>
|
|
47 Haskell とは
|
|
48 </h3>
|
|
49 <p>
|
|
50 純粋関数型プログラミング言語
|
|
51 </p>
|
|
52 <p>
|
|
53 引数に関数を作用させて計算を行います。
|
|
54 </p>
|
|
55 </article>
|
|
56
|
|
57 <article>
|
|
58 <h3>
|
|
59 命令型プログラミングとの違い
|
|
60 </h3>
|
|
61 <p>
|
|
62 命令型プログラミング言語では、命令の並びをコンピュータに与えて、それを実行します。
|
|
63 </p>
|
|
64 <p>
|
|
65 関数型プログラミング言語では、何であるかという定義を与えます。
|
|
66 </p>
|
|
67 <p>
|
|
68 例:n番目のフィボナッチ数を求める関数の定義
|
|
69 <pre>
|
|
70 fib 0 = 0
|
|
71 fib 1 = 1
|
|
72 fib n = fib (n - 1) + fib (n - 2)
|
|
73 </pre>
|
|
74 </p>
|
|
75 </article>
|
|
76
|
|
77 <article>
|
|
78 <h3>
|
|
79 Haskell の特徴
|
|
80 </h3>
|
|
81 <p>
|
|
82 <ul>
|
|
83 <li>副作用を持たない
|
|
84 </ul>
|
|
85 </p>
|
|
86 <p>
|
|
87 関数は副作用を持ちません
|
|
88 </p>
|
|
89 <p>
|
|
90 関数にできることは、何かを計算して結果を返すことだけです。<br>
|
|
91 関数は同じ引数で呼ばれた場合、同じ値を返すことを保証します。
|
|
92 </p>
|
|
93 <p>
|
|
94 関数の正しさを簡単に推察でき、正しいと分かっている関数同士を容易に組み合わせることができます。
|
|
95 </p>
|
|
96 </article>
|
|
97
|
|
98 <article>
|
|
99 <h3>
|
|
100 Haskell の特徴
|
|
101 </h3>
|
|
102 <p>
|
|
103 <ul>
|
|
104 <li>遅延評価
|
|
105 </ul>
|
|
106 </p>
|
|
107 <p>
|
|
108 Haskellは、結果が必要になるまで関数を評価しません。
|
|
109 </p>
|
|
110 <p>
|
|
111 遅延評価のため、無限の大きさのデータを扱うことが可能です。
|
|
112 </p>
|
|
113 <p>
|
|
114 例:奇数の無限のリストから最初の3つを入手する
|
|
115 <pre>
|
|
116 take 3 [1,3..]
|
|
117 </pre>
|
|
118 </p>
|
|
119 </article>
|
|
120
|
|
121 <article>
|
|
122 <h3>
|
|
123 Haskell の特徴
|
|
124 </h3>
|
|
125 <p>
|
|
126 <ul>
|
|
127 <li>静的型付け
|
|
128 </ul>
|
|
129 </p>
|
|
130 <p>
|
|
131 型規則に従ってない式が存在しないことを保証します。
|
|
132 </p>
|
|
133 <p>
|
|
134 また型推論を持つため、すべての式に明示的に型を書く必要はありません。
|
|
135 </p>
|
|
136 <p>
|
|
137 コンパイラが非常に多くのバグを見つけてくれるので、コンパイルが通れば概ね思い通りに動きます。
|
|
138 </p>
|
|
139 </article>
|
|
140
|
|
141 <article>
|
|
142 <h3>
|
|
143 よし、分かった。では Haskell を使う方法を教えてくれ。
|
|
144 </h3>
|
|
145 <p>
|
|
146 一番手っ取り早い方法は、Haskell Platformを導入することです。
|
|
147 </p>
|
|
148 <p>
|
|
149 Haskellのコンパイラで最も広く使われている The Glasgow Haskell Compiler (GHC) や、便利な Haskell のライブラリのセットが付いてきます!
|
|
150 </p>
|
|
151 <p>
|
|
152 <a href="http://www.haskell.org/platform/">http://www.haskell.org/platform/</a>にアクセスして、利用しているOS向けの指示に従ってください。
|
|
153 </p>
|
|
154 </article>
|
|
155
|
|
156 <article>
|
|
157 <h3>
|
|
158 ghciで遊んでみる
|
|
159 </h3>
|
|
160 <p>
|
|
161 <pre>
|
|
162 ghci> 2+3
|
|
163 5
|
|
164 ghci> succ 3
|
|
165 4
|
|
166 ghci> 2 > 3
|
|
167 False
|
|
168 ghci> True && False
|
|
169 False
|
|
170 </pre>
|
|
171 </p>
|
|
172 </article>
|
|
173
|
|
174 <article>
|
|
175 <h3>
|
|
176 ghciで遊んでみる
|
|
177 </h3>
|
|
178 <p>
|
|
179 gchi内で関数を定義する際はletが必要
|
|
180 <pre>
|
|
181 ghci> let doubleMe x = x + x
|
|
182 ghci> doubleMe 4
|
|
183 8
|
|
184 ghci> let doubleUs x y = doubleMe x + doubleMe y
|
|
185 ghci> doubleUs 2 3
|
|
186 10
|
|
187 ghci> doubleUs 1.2 4.5
|
|
188 11.4
|
|
189 </pre>
|
|
190 </p>
|
|
191 </article>
|
|
192 <article>
|
|
193 <h3>
|
|
194 Haskell Platform をインストールしたら
|
|
195 </h3>
|
|
196 <p>
|
|
197 Terminal を開き、
|
|
198 <pre>
|
|
199 $ ghci
|
|
200 </pre>
|
|
201 とタイプすることで、対話モードが起動できます。
|
|
202 </p>
|
|
203 <p>
|
|
204 対話モードでは、実際に関数を呼び出して、結果を直接見ることができます。
|
|
205 </p>
|
|
206 <p>
|
|
207 対話モードを終了するには、
|
|
208 <pre>
|
|
209 ghci> :q
|
|
210 </pre>
|
|
211 とタイプし、ENTERを押します。
|
|
212 </p>
|
|
213 </article>
|
|
214
|
|
215 <article>
|
|
216 <h3>
|
|
217 なぜ Haskell で Web Service を書くのか
|
|
218 </h3>
|
|
219 </article>
|
|
220
|
|
221 <article>
|
|
222 <h3>
|
|
223 Haskell の Web Framework
|
|
224 </h3>
|
|
225 </article>
|
|
226
|
|
227 <article>
|
|
228 <h3>
|
|
229 Warp
|
|
230 </h3>
|
|
231 <p>
|
|
232 軽量、高速 HTTP サーバです。
|
|
233 </p>
|
|
234 <p>
|
|
235 Haskell の軽量スレッドを活かして書かれています。
|
|
236 </p>
|
|
237 <p>
|
|
238 Pong benchmark (req/s)
|
|
239 <img class='centered' src='pic/pong.png'>
|
|
240 </p>
|
|
241 </article>
|
|
242
|
|
243 <article>
|
|
244 <h3>
|
|
245 とりあえず、Warp 入れてみる
|
|
246 </h3>
|
|
247 <pre>
|
|
248 $ cabal install warp
|
|
249 </pre>
|
|
250 </article>
|
|
251
|
|
252 <article class="smaller">
|
|
253 <h3>
|
|
254 簡単なプログラム
|
|
255 </h3>
|
|
256 <pre>
|
|
257 {-# LANGUAGE OverloadedStrings #-}
|
|
258 import Network.Wai
|
|
259 import Network.HTTP.Types (status200)
|
|
260 import Network.Wai.Handler.Warp (run)
|
|
261
|
|
262 application _ = return $
|
|
263 responseLBS status200 [("Content-Type", "text/plain")] "Hello World"
|
|
264
|
|
265 main = run 3000 application
|
|
266 </pre>
|
|
267 <p>
|
|
268 このソースコードを読み解いていきます。
|
|
269 </p>
|
|
270 <p>
|
|
271 Haskell は型を見れば多くのことが分かる言語です。<br>
|
|
272 ということで、関数の型を見ていきます!
|
|
273 </p>
|
|
274 </article>
|
|
275
|
|
276 <article class="smaller">
|
|
277 <h3>
|
|
278 関数の型を確認する
|
|
279 </h3>
|
|
280 <p>
|
|
281 まず、対話モードを開きます。
|
|
282 </p>
|
|
283 <pre>
|
|
284 $ ghci
|
|
285 </pre>
|
|
286 <p>
|
|
287 Wai 及び Warp の module を import します。<br>
|
|
288 Wai というのは、Web Application Interface の略で、Web サーバと Web アプリケーション間の共通のプロトコルを取り扱います。
|
|
289 </p>
|
|
290 <pre>
|
|
291 ghci> :module +Network.Wai
|
|
292 ghci> :module +Network.Wai.Handler.Warp
|
|
293 </pre>
|
|
294 <p>
|
|
295 型を教えて貰うには、:t コマンドに続けて正しい式を入力します。
|
|
296 </p>
|
|
297 <pre>
|
|
298 ghci> :t run
|
|
299 run :: Port -> Application -> IO ()
|
|
300 </pre>
|
|
301 </article>
|
|
302
|
|
303 <article class="smaller">
|
|
304 <h3>
|
|
305 run
|
|
306 </h3>
|
|
307 <pre>
|
|
308 ghci> :t run
|
|
309 run :: Port -> Application -> IO ()
|
|
310 </pre>
|
|
311 <p>
|
|
312 run は、Port と Application を受け取って、IO () を返す関数だということが分かります。
|
|
313 </p>
|
|
314 <p>
|
|
315 Port は、Int の別名です。<br>
|
|
316 別名などの定義は、:t ではみれないので、:i を使うとよいでしょう。<br>
|
|
317 Haskellでは、関数は小文字、型名などは大文字で始まります。
|
|
318 </p>
|
|
319 </article>
|
|
320 <article class="smaller">
|
|
321 <h3>
|
|
322 Application は、なんでしょうか?
|
|
323 </h3>
|
|
324 <pre>
|
|
325 ghci> :i Application
|
|
326 type Application =
|
|
327 Request -> Control.Monad.Trans.Resource.ResourceT IO Response
|
|
328 </pre>
|
|
329 <p>
|
|
330 Monad に包まれていて少しわかりにくいですが、端的に言えば Request を受け取って Response を返す関数を表しています。
|
|
331 </p>
|
|
332 <p>
|
|
333 ResourceT は、リソースの解放を取り扱う Monad、 IO は、入出力を取り扱う Monad です。
|
|
334 </p>
|
|
335 <p>
|
|
336 Haskell では、関数が副作用を持つことは許されませんが、IO アクションによって IO 操作を処理系に押し付けています。
|
|
337 </p>
|
|
338 </article>
|
|
339
|
|
340 <article class="smaller">
|
|
341 <h3>
|
|
342 runとは結局なんぞや
|
|
343 </h3>
|
|
344 <pre>
|
|
345 {-# LANGUAGE OverloadedStrings #-}
|
|
346 import Network.Wai
|
|
347 import Network.HTTP.Types (status200)
|
|
348 import Network.Wai.Handler.Warp (run)
|
|
349
|
|
350 application _ = return $
|
|
351 responseLBS status200 [("Content-Type", "text/plain")] "Hello World"
|
|
352
|
|
353 main = run 3000 application
|
|
354 </pre>
|
|
355 <p>
|
|
356 型情報を読むことでおおよそ分かったような気がしませんか!
|
|
357 </p>
|
|
358 <p>
|
|
359 Port 番号と、Request を受け取って Response 返す関数を受け取る<br>
|
|
360 run は IO () を返すので、外界に影響を与える
|
|
361 </p>
|
|
362 <p>
|
|
363 実際の動作としては、この関数 run は受け取った Port 番号で、Application を実行します。
|
|
364 </p>
|
|
365 <p>
|
|
366 次に、Application の実装を見ていきます。
|
|
367 </p>
|
|
368 </article>
|
|
369
|
|
370 <article class="smaller">
|
|
371 <h3>
|
|
372 Hello Worldと出力する簡単なお仕事
|
|
373 </h3>
|
|
374 <pre>
|
|
375 application _ = return $
|
|
376 responseLBS status200 [("Content-Type", "text/plain")] "Hello World"
|
|
377 </pre>
|
|
378 <p>
|
|
379 まず引数で渡される Request は _ (Underscore) となっているので、使用していません。
|
|
380 </p>
|
|
381 </article>
|
|
382
|
|
383 <article class="smaller">
|
|
384 <h3>
|
|
385 responseLBS とは?
|
|
386 </h3>
|
|
387 <pre>
|
|
388 Prelude Network.Wai.Handler.Warp Network.Wai Network.HTTP.Types> :t responseLBS
|
|
389 responseLBS
|
|
390 :: Status
|
|
391 -> ResponseHeaders
|
|
392 -> Data.ByteString.Lazy.Internal.ByteString
|
|
393 -> Response
|
|
394 </pre>
|
|
395 <p>
|
|
396 Statusと、ResponseHeaders、ByteStringを受け取り、Responseを返します。
|
|
397 </p>
|
|
398 <p>
|
|
399 簡単に説明すると、文字列からResponseを構築するためのコンストラクターです。
|
|
400 </p>
|
|
401 <p>
|
|
402 ByteStringは、Stringと比較して効率よく文字列を扱います。
|
|
403 プログラムの最初に書かれている OverloadedStrings という言語拡張は、ダブルクオートで囲んだ文字列を、ByteString リテラルとして扱ってくれます。
|
|
404 </p>
|
|
405 <pre>
|
|
406 {-# LANGUAGE OverloadedStrings #-}
|
|
407 </pre>
|
|
408 </article>
|
|
409
|
|
410 <article class="smaller">
|
|
411 <h3>
|
|
412 Hello.hs
|
|
413 </h3>
|
|
414 <pre>
|
|
415 {-# LANGUAGE OverloadedStrings #-}
|
|
416 import Network.Wai
|
|
417 import Network.HTTP.Types (status200)
|
|
418 import Network.Wai.Handler.Warp (run)
|
|
419
|
|
420 application _ = return $
|
|
421 responseLBS status200 [("Content-Type", "text/plain")] "Hello World"
|
|
422
|
|
423 main = run 3000 application
|
|
424 </pre>
|
|
425 </article>
|
|
426
|
|
427 <article>
|
|
428 <h3>
|
|
429 おおよその内容は掴めた、では実行してみようではないか。
|
|
430 </h3>
|
|
431 <p>
|
|
432 実行方法は2つあります。
|
|
433 </p>
|
|
434 <p>
|
|
435 <ul>
|
|
436 <li>コンパイルせずに実行
|
|
437 </ul>
|
|
438 <pre>
|
|
439 $ runghc Hello.hs
|
|
440 </pre>
|
|
441 </p>
|
|
442 <p>
|
|
443 <ul>
|
|
444 <li>コンパイルして実行
|
|
445 </ul>
|
|
446 <pre>
|
|
447 $ ghc --make Hello.hs
|
|
448 $ ./Hello
|
|
449 </pre>
|
|
450 </p>
|
|
451 </article>
|
|
452 <article>
|
|
453 <h3>
|
|
454 サイトにアクセスしてみる
|
|
455 </h3>
|
|
456 <p>
|
|
457 <a href="http://localhost:3000/">http://localhost:3000/</a>
|
|
458 </p>
|
|
459 <p>
|
|
460 Hello Worldと表示されれば成功です。
|
|
461 </p>
|
|
462 </article>
|
|
463
|
|
464 <article>
|
|
465 <h3>
|
|
466 さすがにこれは簡単すぎる…
|
|
467 </h3>
|
|
468 <p>
|
|
469 こんなの30秒で書けるよという皆様のために、次に単純な Routing っぽいものを実装してみたいと思います。
|
|
470 </p>
|
|
471 <p>
|
|
472 Application が受け取る Request には、様々な情報が含まれています。<br>
|
|
473 その中には pathInfo という、どこの path へアクセスしてきたかの情報があります。
|
|
474 </p>
|
|
475 </article>
|
|
476
|
|
477 <article>
|
|
478 <h3>
|
|
479 pathInfoの情報をどう入手するか
|
|
480 </h3>
|
|
481 <p>
|
|
482 実は超簡単です。
|
|
483 </p>
|
|
484 <pre>
|
|
485 pathInfo request
|
|
486 </pre>
|
|
487 <p>
|
|
488 pathInfo という関数に Request を渡すだけ!
|
|
489 </p>
|
|
490 <p>
|
|
491 Haskell では、レコード構文というデータ型のフィールドにアクセスするためのコードを自動生成する仕組みがあります。
|
|
492 レコード構文で、データ型を定義するとデータ型が作られるのと同時に、フィールド名でフィールドを取得する関数たちが自動で作られます。
|
|
493 </p>
|
|
494 <p>
|
|
495 <pre>
|
|
496 ghci> :t pathInfo
|
|
497 pathInfo :: Request -> [Data.Text.Internal.Text]
|
|
498 </pre>
|
|
499 </p>
|
|
500 </article>
|
|
501
|
|
502 <article>
|
|
503 <h3>
|
|
504 pathによって返す関数を変えてみよう
|
|
505 </h3>
|
|
506 <p>
|
|
507 まず、application で request を取り routes という関数に path を渡すように変更します。
|
|
508 </p>
|
|
509 <pre>
|
|
510 application request = return $
|
|
511 routes $ pathInfo request
|
|
512 </pre>
|
|
513 <p>
|
|
514 routes は、path を受け取って response を返す関数です。
|
|
515 </p>
|
|
516 <pre>
|
|
517 routes path = findRoute path routeSetting
|
|
518 </pre>
|
|
519 <p>
|
|
520 routes では、routeSetting という List から path が一致するもの探します。
|
|
521 </p>
|
|
522 </article>
|
|
523
|
|
524 <article>
|
|
525 <h3>
|
|
526 pathによって返す関数を変えてみよう
|
|
527 </h3>
|
|
528 <p>
|
|
529 findRoute では、再帰的に List を探索しています。<br>
|
|
530 一致するものがなければ、notFound という関数を返します。<br>
|
|
531 一致するものがあれば、routeSetting に記載された関数を返します。
|
|
532 <pre>
|
|
533 findRoute path [] = notFound
|
|
534 findRoute path ((p,f):xs)
|
|
535 | path == p = f
|
|
536 | otherwise = findRoute path xs
|
|
537
|
|
538 routeSetting = [([], index),
|
|
539 (["hello"], hello),
|
|
540 (["welcome","world"],world)]
|
|
541 </pre>
|
|
542 </article>
|
|
543
|
|
544 <article class="smaller">
|
|
545 <h3>
|
|
546 response関数を定義しよう
|
|
547 </h3>
|
|
548 <p>
|
|
549 いくつか定義してみます。
|
|
550 </p>
|
|
551 <pre>
|
|
552 notFound = do
|
|
553 responseLBS status404 [("Content-type", "text/html")] $ "404 - File Not Found"
|
|
554
|
|
555 index = do
|
|
556 responseLBS status200 [("Content-type", "text/html")] $ "index page"
|
|
557
|
|
558 hello = do
|
|
559 responseLBS status200 [("Content-type", "text/html")] $ "hello, my name is Tom"
|
|
560
|
|
561 world = do
|
|
562 responseLBS status200 [("Content-type", "text/html")] $ "Welcome to Underground"
|
|
563 </pre>
|
|
564 </article>
|
|
565
|
|
566 <article class="smaller">
|
|
567 <h3>
|
|
568 完成!
|
|
569 </h3>
|
|
570 <p>
|
|
571 <a href="https://gist.github.com/amothic/5933808">Source Code</a>
|
|
572 <pre>
|
|
573 application request = return $
|
|
574 routes $ pathInfo request
|
|
575
|
|
576 routes path = findRoute path routeSetting
|
|
577
|
|
578 findRoute path [] = notFound
|
|
579 findRoute path ((p,f):xs)
|
|
580 | path == p = f
|
|
581 | otherwise = findRoute path xs
|
|
582 routeSetting = [([], index),
|
|
583 (["hello"], hello),
|
|
584 (["welcome","world"],world)]
|
|
585 notFound = do
|
|
586 responseLBS status404 [("Content-type", "text/html")] $ "404 - File Not Found"
|
|
587 index = do
|
|
588 responseLBS status200 [("Content-type", "text/html")] $ "index page"
|
|
589 hello = do
|
|
590 responseLBS status200 [("Content-type", "text/html")] $ "hello, my name is Tom"
|
|
591 world = do
|
|
592 responseLBS status200 [("Content-type", "text/html")] $ "Welcome to Underground"
|
|
593
|
|
594 main = run 3000 application
|
|
595 </pre>
|
|
596 </p>
|
|
597 </article>
|
|
598
|
|
599 <article>
|
|
600 <h3>
|
|
601 実際に動かしてみる!
|
|
602 </h3>
|
|
603 <p>
|
|
604 シンプルなシステムですが、実際に動くのか確かめてみます。
|
|
605 </p>
|
|
606 <p>
|
|
607 "index page" と表示されるはず<br>
|
|
608 <a href="http://localhost:3000/">http://localhost:3000/</a>
|
|
609 </p>
|
|
610 <p>
|
|
611 "hello, my name is Tom" と表示されるはず<br>
|
|
612 <a href="http://localhost:3000/hello">http://localhost:3000/hello</a>
|
|
613 </p>
|
|
614 <p>
|
|
615 "welcome to Underground" と表示されるはず<br>
|
|
616 <a href="http://localhost:3000/welcome/world">http://localhost:3000/welcome/world</a>
|
|
617 </p>
|
|
618 <p>
|
|
619 一致するpathがないので、"404 - File Not Found" と表示されるはず<br>
|
|
620 <a href="http://localhost:3000/hogehoge">http://localhost:3000/hogehoge</a>
|
|
621 </p>
|
|
622 </article>
|
|
623 <article>
|
|
624 <h3>
|
|
625 そして気づくことなど
|
|
626 </h3>
|
|
627 <p>
|
|
628 あれ、Haskellのプログラムって短くね?
|
|
629 </p>
|
|
630 <p>
|
|
631 そうです、圧倒的な記述力も特徴なんです。
|
|
632 </p>
|
|
633 <p>
|
|
634 速くて安全なHaskellで、あなたもWeb Serviceを作って見ませんか?
|
|
635 </p>
|
|
636 </article>
|
|
637
|
|
638 <article>
|
|
639 <h3>
|
|
640 参考文献
|
|
641 </h3>
|
|
642 <p>
|
|
643 <ul>
|
|
644 <li>田中 英行, 村主 崇行(2012) 『すごいHaskellたのしく学ぼう!』 オーム社
|
|
645 <li><a href="http://mew.org/~kazu/material/2011-ll-haskell.pdf">メタプログラミングの光と闇 ~ Haskell 編~</a>
|
|
646 <li><a href="http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks">Preliminary Warp Cross-Language Benchmarks</a>
|
|
647 <li><a href="http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/">YBlog - Haskell web programming</a>
|
|
648 </ul>
|
|
649 </p>
|
|
650 </article>
|
|
651 </section>
|
|
652 </body>
|
|
653 </html>
|