Mercurial > hg > Members > toma > osc2013
view haskell.html @ 0:c0be45e5b32f
Initial commit
author | Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 05 Jul 2013 21:23:19 +0900 |
parents | |
children | a69b147cd82e |
line wrap: on
line source
<!DOCTYPE html> <!-- Google HTML5 slide template Authors: Luke Mahé (code) Marcin Wichary (code and design) Dominic Mazzoni (browser compatibility) Charles Chen (ChromeVox support) URL: http://code.google.com/p/html5slides/ --> <html> <head> <title>Haskell による Web Service 構築入門</title> <meta charset='utf-8'> <script src='http://web.amothic.com/html5slides/slides.js'></script> </head> <style> /* Your individual styles here, or just use inline styles if that’s what you want. */ </style> <body style='display: none'> <section class='slides layout-regular template-concurrency'> <article> <h1> Haskell による Web Service 構築入門 </h1> <p> Daichi TOMA <br> Jul 6, 2013 </p> </article> <article> <h3> Haskell とは </h3> <p> 純粋関数型プログラミング言語 </p> <p> 引数に関数を作用させて計算を行います。 </p> </article> <article> <h3> 命令型プログラミングとの違い </h3> <p> 命令型プログラミング言語では、命令の並びをコンピュータに与えて、それを実行します。 </p> <p> 関数型プログラミング言語では、何であるかという定義を与えます。 </p> <p> 例:n番目のフィボナッチ数を求める関数の定義 <pre> fib 0 = 0 fib 1 = 1 fib n = fib (n - 1) + fib (n - 2) </pre> </p> </article> <article> <h3> Haskell の特徴 </h3> <p> <ul> <li>副作用を持たない </ul> </p> <p> 関数は副作用を持ちません </p> <p> 関数にできることは、何かを計算して結果を返すことだけです。<br> 関数は同じ引数で呼ばれた場合、同じ値を返すことを保証します。 </p> <p> 関数の正しさを簡単に推察でき、正しいと分かっている関数同士を容易に組み合わせることができます。 </p> </article> <article> <h3> Haskell の特徴 </h3> <p> <ul> <li>遅延評価 </ul> </p> <p> Haskellは、結果が必要になるまで関数を評価しません。 </p> <p> 遅延評価のため、無限の大きさのデータを扱うことが可能です。 </p> <p> 例:奇数の無限のリストから最初の3つを入手する <pre> take 3 [1,3..] </pre> </p> </article> <article> <h3> Haskell の特徴 </h3> <p> <ul> <li>静的型付け </ul> </p> <p> 型規則に従ってない式が存在しないことを保証します。 </p> <p> また型推論を持つため、すべての式に明示的に型を書く必要はありません。 </p> <p> コンパイラが非常に多くのバグを見つけてくれるので、コンパイルが通れば概ね思い通りに動きます。 </p> </article> <article> <h3> よし、分かった。では Haskell を使う方法を教えてくれ。 </h3> <p> 一番手っ取り早い方法は、Haskell Platformを導入することです。 </p> <p> Haskellのコンパイラで最も広く使われている The Glasgow Haskell Compiler (GHC) や、便利な Haskell のライブラリのセットが付いてきます! </p> <p> <a href="http://www.haskell.org/platform/">http://www.haskell.org/platform/</a>にアクセスして、利用しているOS向けの指示に従ってください。 </p> </article> <article> <h3> ghciで遊んでみる </h3> <p> <pre> ghci> 2+3 5 ghci> succ 3 4 ghci> 2 > 3 False ghci> True && False False </pre> </p> </article> <article> <h3> ghciで遊んでみる </h3> <p> gchi内で関数を定義する際はletが必要 <pre> ghci> let doubleMe x = x + x ghci> doubleMe 4 8 ghci> let doubleUs x y = doubleMe x + doubleMe y ghci> doubleUs 2 3 10 ghci> doubleUs 1.2 4.5 11.4 </pre> </p> </article> <article> <h3> Haskell Platform をインストールしたら </h3> <p> Terminal を開き、 <pre> $ ghci </pre> とタイプすることで、対話モードが起動できます。 </p> <p> 対話モードでは、実際に関数を呼び出して、結果を直接見ることができます。 </p> <p> 対話モードを終了するには、 <pre> ghci> :q </pre> とタイプし、ENTERを押します。 </p> </article> <article> <h3> なぜ Haskell で Web Service を書くのか </h3> </article> <article> <h3> Haskell の Web Framework </h3> </article> <article> <h3> Warp </h3> <p> 軽量、高速 HTTP サーバです。 </p> <p> Haskell の軽量スレッドを活かして書かれています。 </p> <p> Pong benchmark (req/s) <img class='centered' src='pic/pong.png'> </p> </article> <article> <h3> とりあえず、Warp 入れてみる </h3> <pre> $ cabal install warp </pre> </article> <article class="smaller"> <h3> 簡単なプログラム </h3> <pre> {-# LANGUAGE OverloadedStrings #-} import Network.Wai import Network.HTTP.Types (status200) import Network.Wai.Handler.Warp (run) application _ = return $ responseLBS status200 [("Content-Type", "text/plain")] "Hello World" main = run 3000 application </pre> <p> このソースコードを読み解いていきます。 </p> <p> Haskell は型を見れば多くのことが分かる言語です。<br> ということで、関数の型を見ていきます! </p> </article> <article class="smaller"> <h3> 関数の型を確認する </h3> <p> まず、対話モードを開きます。 </p> <pre> $ ghci </pre> <p> Wai 及び Warp の module を import します。<br> Wai というのは、Web Application Interface の略で、Web サーバと Web アプリケーション間の共通のプロトコルを取り扱います。 </p> <pre> ghci> :module +Network.Wai ghci> :module +Network.Wai.Handler.Warp </pre> <p> 型を教えて貰うには、:t コマンドに続けて正しい式を入力します。 </p> <pre> ghci> :t run run :: Port -> Application -> IO () </pre> </article> <article class="smaller"> <h3> run </h3> <pre> ghci> :t run run :: Port -> Application -> IO () </pre> <p> run は、Port と Application を受け取って、IO () を返す関数だということが分かります。 </p> <p> Port は、Int の別名です。<br> 別名などの定義は、:t ではみれないので、:i を使うとよいでしょう。<br> Haskellでは、関数は小文字、型名などは大文字で始まります。 </p> </article> <article class="smaller"> <h3> Application は、なんでしょうか? </h3> <pre> ghci> :i Application type Application = Request -> Control.Monad.Trans.Resource.ResourceT IO Response </pre> <p> Monad に包まれていて少しわかりにくいですが、端的に言えば Request を受け取って Response を返す関数を表しています。 </p> <p> ResourceT は、リソースの解放を取り扱う Monad、 IO は、入出力を取り扱う Monad です。 </p> <p> Haskell では、関数が副作用を持つことは許されませんが、IO アクションによって IO 操作を処理系に押し付けています。 </p> </article> <article class="smaller"> <h3> runとは結局なんぞや </h3> <pre> {-# LANGUAGE OverloadedStrings #-} import Network.Wai import Network.HTTP.Types (status200) import Network.Wai.Handler.Warp (run) application _ = return $ responseLBS status200 [("Content-Type", "text/plain")] "Hello World" main = run 3000 application </pre> <p> 型情報を読むことでおおよそ分かったような気がしませんか! </p> <p> Port 番号と、Request を受け取って Response 返す関数を受け取る<br> run は IO () を返すので、外界に影響を与える </p> <p> 実際の動作としては、この関数 run は受け取った Port 番号で、Application を実行します。 </p> <p> 次に、Application の実装を見ていきます。 </p> </article> <article class="smaller"> <h3> Hello Worldと出力する簡単なお仕事 </h3> <pre> application _ = return $ responseLBS status200 [("Content-Type", "text/plain")] "Hello World" </pre> <p> まず引数で渡される Request は _ (Underscore) となっているので、使用していません。 </p> </article> <article class="smaller"> <h3> responseLBS とは? </h3> <pre> Prelude Network.Wai.Handler.Warp Network.Wai Network.HTTP.Types> :t responseLBS responseLBS :: Status -> ResponseHeaders -> Data.ByteString.Lazy.Internal.ByteString -> Response </pre> <p> Statusと、ResponseHeaders、ByteStringを受け取り、Responseを返します。 </p> <p> 簡単に説明すると、文字列からResponseを構築するためのコンストラクターです。 </p> <p> ByteStringは、Stringと比較して効率よく文字列を扱います。 プログラムの最初に書かれている OverloadedStrings という言語拡張は、ダブルクオートで囲んだ文字列を、ByteString リテラルとして扱ってくれます。 </p> <pre> {-# LANGUAGE OverloadedStrings #-} </pre> </article> <article class="smaller"> <h3> Hello.hs </h3> <pre> {-# LANGUAGE OverloadedStrings #-} import Network.Wai import Network.HTTP.Types (status200) import Network.Wai.Handler.Warp (run) application _ = return $ responseLBS status200 [("Content-Type", "text/plain")] "Hello World" main = run 3000 application </pre> </article> <article> <h3> おおよその内容は掴めた、では実行してみようではないか。 </h3> <p> 実行方法は2つあります。 </p> <p> <ul> <li>コンパイルせずに実行 </ul> <pre> $ runghc Hello.hs </pre> </p> <p> <ul> <li>コンパイルして実行 </ul> <pre> $ ghc --make Hello.hs $ ./Hello </pre> </p> </article> <article> <h3> サイトにアクセスしてみる </h3> <p> <a href="http://localhost:3000/">http://localhost:3000/</a> </p> <p> Hello Worldと表示されれば成功です。 </p> </article> <article> <h3> さすがにこれは簡単すぎる… </h3> <p> こんなの30秒で書けるよという皆様のために、次に単純な Routing っぽいものを実装してみたいと思います。 </p> <p> Application が受け取る Request には、様々な情報が含まれています。<br> その中には pathInfo という、どこの path へアクセスしてきたかの情報があります。 </p> </article> <article> <h3> pathInfoの情報をどう入手するか </h3> <p> 実は超簡単です。 </p> <pre> pathInfo request </pre> <p> pathInfo という関数に Request を渡すだけ! </p> <p> Haskell では、レコード構文というデータ型のフィールドにアクセスするためのコードを自動生成する仕組みがあります。 レコード構文で、データ型を定義するとデータ型が作られるのと同時に、フィールド名でフィールドを取得する関数たちが自動で作られます。 </p> <p> <pre> ghci> :t pathInfo pathInfo :: Request -> [Data.Text.Internal.Text] </pre> </p> </article> <article> <h3> pathによって返す関数を変えてみよう </h3> <p> まず、application で request を取り routes という関数に path を渡すように変更します。 </p> <pre> application request = return $ routes $ pathInfo request </pre> <p> routes は、path を受け取って response を返す関数です。 </p> <pre> routes path = findRoute path routeSetting </pre> <p> routes では、routeSetting という List から path が一致するもの探します。 </p> </article> <article> <h3> pathによって返す関数を変えてみよう </h3> <p> findRoute では、再帰的に List を探索しています。<br> 一致するものがなければ、notFound という関数を返します。<br> 一致するものがあれば、routeSetting に記載された関数を返します。 <pre> findRoute path [] = notFound findRoute path ((p,f):xs) | path == p = f | otherwise = findRoute path xs routeSetting = [([], index), (["hello"], hello), (["welcome","world"],world)] </pre> </article> <article class="smaller"> <h3> response関数を定義しよう </h3> <p> いくつか定義してみます。 </p> <pre> notFound = do responseLBS status404 [("Content-type", "text/html")] $ "404 - File Not Found" index = do responseLBS status200 [("Content-type", "text/html")] $ "index page" hello = do responseLBS status200 [("Content-type", "text/html")] $ "hello, my name is Tom" world = do responseLBS status200 [("Content-type", "text/html")] $ "Welcome to Underground" </pre> </article> <article class="smaller"> <h3> 完成! </h3> <p> <a href="https://gist.github.com/amothic/5933808">Source Code</a> <pre> application request = return $ routes $ pathInfo request routes path = findRoute path routeSetting findRoute path [] = notFound findRoute path ((p,f):xs) | path == p = f | otherwise = findRoute path xs routeSetting = [([], index), (["hello"], hello), (["welcome","world"],world)] notFound = do responseLBS status404 [("Content-type", "text/html")] $ "404 - File Not Found" index = do responseLBS status200 [("Content-type", "text/html")] $ "index page" hello = do responseLBS status200 [("Content-type", "text/html")] $ "hello, my name is Tom" world = do responseLBS status200 [("Content-type", "text/html")] $ "Welcome to Underground" main = run 3000 application </pre> </p> </article> <article> <h3> 実際に動かしてみる! </h3> <p> シンプルなシステムですが、実際に動くのか確かめてみます。 </p> <p> "index page" と表示されるはず<br> <a href="http://localhost:3000/">http://localhost:3000/</a> </p> <p> "hello, my name is Tom" と表示されるはず<br> <a href="http://localhost:3000/hello">http://localhost:3000/hello</a> </p> <p> "welcome to Underground" と表示されるはず<br> <a href="http://localhost:3000/welcome/world">http://localhost:3000/welcome/world</a> </p> <p> 一致するpathがないので、"404 - File Not Found" と表示されるはず<br> <a href="http://localhost:3000/hogehoge">http://localhost:3000/hogehoge</a> </p> </article> <article> <h3> そして気づくことなど </h3> <p> あれ、Haskellのプログラムって短くね? </p> <p> そうです、圧倒的な記述力も特徴なんです。 </p> <p> 速くて安全なHaskellで、あなたもWeb Serviceを作って見ませんか? </p> </article> <article> <h3> 参考文献 </h3> <p> <ul> <li>田中 英行, 村主 崇行(2012) 『すごいHaskellたのしく学ぼう!』 オーム社 <li><a href="http://mew.org/~kazu/material/2011-ll-haskell.pdf">メタプログラミングの光と闇 ~ Haskell 編~</a> <li><a href="http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks">Preliminary Warp Cross-Language Benchmarks</a> <li><a href="http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/">YBlog - Haskell web programming</a> </ul> </p> </article> </section> </body> </html>