2017年1月9日月曜日

STEP UP Haskell!! -2-

mainpicture

先日から引き続きhaskellを使っていこう。

yesodというwebのフレームワークを利用しながら開発をしていくつもりなので、

この二つのアプローチで学習していく


STEP UP Haskell!! -2-

  1. yesod環境の構築
  2. helloworld.hs
    1. sample1 helloworld
    2. sample2 links
    3. sample3 json

yesod環境の構築

まずはyesodでの開発を可能にする。
Yesod quickstartに従えばできるはず。

….

と思っていた時期が私にもありました。

私の場合はstack build yesod-bin cabal-install --install-ghcここで失敗した。。。

理由はGHC panic when building Stack on macOS Sierraというghc7系とMac OS Sierraの相性の問題の模様。

最新版の8.0.2では修正が入っているようだが、まだリリースされていない(2017/01/08時点)
また、8.0.2で仮に動作したとしてもyesodに必要なlib郡が全部入るのかどうかも分からない。
stackageにはまだ8.0.2のyesod libが定義されていないし)

詰んだ。と思ったが要はOSを変えればいいのだ。
なのでwindowsに逃げるなりvmを立ち上げるなりをすればいいのだ。

調べたところによるとhaskell stackがdockerサポートを結構やっているらしく、
optionを一つ追加するだけでstackコマンド全般がdockerのcontainer上で動作するように設定が可能っぽい。

以下のような手順でとりあえずyesodサンプル(db migrateからlocalhost:3000でのアクセスまで)ができるようにした。

docker install
 (これは各OS毎に異なるかも、Macはdocker platformがあったのでdmgからinstallした)

stack docker pull
(stack.ymlに準拠したfpco/stack-buildという名称のimageが取得できる)

stack new my-project yesod-mysql && cd my-project

ここまでやったらstack.ymlが出来上がるので、

docker:
  enable: true

これを追記。

x stack build yesod-bin cabal-install --install-ghc
(マニュアルによるとこれだが、install-ghcだとstackがいれたやつになりそうだったからやめた)

stack build yesod-bin cabal-install
(こっちにした)

stack build

これでstack関係はOK。

# ここからmysqlのinstall
brew install mysql
mysqld --initialize --explicit_defaults_for_timestamp
(passwordメモっとくこと)
mysql_secure_installation
mysqld

これでmysqlは起動する。

で、mysqlの設定をyesodプロジェクトの方に記載する必要があるので、
config/setting.ymlの該当箇所を以下のように編集

database:
  user:     "_env:MYSQL_USER:yourusername"
  password: "_env:MYSQL_PASSWORD:yourpassword"
  host:     "_env:MYSQL_HOST:192.168.11.2" # your host IP address, not hostname
  port:     "_env:MYSQL_PORT:3306"
  # See config/test-settings.yml for an override during tests
  database: "_env:MYSQL_DATABASE:yourdbname"
  poolsize: "_env:MYSQL_POOLSIZE:10"

hostはlocalhostだとsocketを利用しにいくので、必ずIPで指定。
というのもdocker上で動いているcontainerからhostマシンのmysqlに接続にいくので
sockerファイルの共有の設定がめんどくさいからだ。

ここのIPアドレスはhostマシンでifconfigで出てるやつを設定しておけばいい。
公式のドキュメントによればcontainerとhostマシンのネットワークは同様に使えるとのことだった。

最後に

stack --docker-run-args='--net=bridge --publish=3000:3000' exec -- yesod devel

この--docker-run-argsとか不要だと思うのだけど何故かつけないと
yesodのdevel serverにhostマシンのブラウザからアクセスができなかった。

refs: http://qiita.com/tanakh/items/6866d0f570d0547df026

今後の世界を考えてもdocker imageでcontainerを作ったり破棄したりを繰り返すようになるだろうし、docker imageの中に含まれていてもなんら問題はないだろう。
(むしろdeploy時にも楽ができるようになるんじゃないかと目論んでいる)
どちらにせよserveするときにはimage化したいし。

refs: https://docs.haskellstack.org/en/stable/docker_integration/

以降はチュートリアルをやってみる。


helloworld.hs

railsとかと違って全然知識不足により即実装とはなかなか行きづらいのでチュートリアル


sample1 helloworld

{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeFamilies          #-}
import           Yesod

data HelloWorld = HelloWorld

mkYesod "HelloWorld" [parseRoutes|
/ HomeR GET
|]

instance Yesod HelloWorld

getHomeR :: Handler Html
getHomeR = defaultLayout [whamlet|Hello World!|]

main :: IO ()
main = warp 3000 HelloWorld

これをコピペして実行。

解説が載っているので割愛するが、一部分からないのがQuasiQuotesという単語。

準クォートという名称らしいのだが、
http://d.hatena.ne.jp/ruicc/20111015/1318630433
http://haskell.g.hatena.ne.jp/mr_konn/20101210/quasiquotes
http://www.slideshare.net/konn/metaprogramming-in-haskell

この辺りの解説くらいしか目ぼしいものが見つからない。

基礎勉強の方のアプローチでそのうち出てくることを祈ろう。
とっても薄い理解だと、クォート(”)は文字列リテラルを表しているように、
何か別な記号や関数名(?)に意味を持たせて実行可能にする。
(クォートではないが表記としては同じような書き方ができるから準クォート?)

間違いが発覚したり、後日理解が進んだら訂正します。

この準クォートに当たるのがparseRoutes。
名前からするとyesodが用意してくれたpath名のparserか。
HomeR/GETリクエストが来たら答える。というコード。

HomeRのRはresourceの意味らしい。慣習的につけているっぽい

規約により、http methodとresource nameをつなげてhandlerを定義する。
今回はGET + HomeRなのでgetHomeRをhandlerとして定義している。
whamletはQuasiQuotesらしい。(やっぱ微妙にわかりにくい。。。)
このケースだとHamlet Syntax(Hamletはyesodのtemplate engine)からWidgetを作り出すとのこと。

時たま解説にTH functionというのが出てくるがおそらくTemplateHaskell functionの略っぽい。

ddump-splicesこのオプションを入れてghcを実行すると構文木を接合した状態のものを出力してくれる。
上記準クォートの処理結果を必要なところで展開して見せてくれるような機能だと理解した。
stackコマンドを利用しているならstack runghc -- -ddump-splices helloworld.hsで実行可能だ。

あとの細かいことは後述されるとのことだったので、他のチャプターを読んだ時に出てくるのだろう。


{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeFamilies          #-}
import           Yesod

data Links = Links

mkYesod "Links" [parseRoutes|
/ HomeR GET
/page1 Page1R GET
/page2 Page2R GET
|]

instance Yesod Links

getHomeR  = defaultLayout [whamlet|<a href=@{Page1R}>Go to page 1!|]
getPage1R = defaultLayout [whamlet|<a href=@{Page2R}>Go to page 2!|]
getPage2R = defaultLayout [whamlet|<a href=@{HomeR}>Go home!|]

main = warp 3000 Links

Linksアプリケーションが出来上がった。

注目すべきは

  • /page1/page2が出来上がっていること
  • aタグの中にdata constructorであるPage1Rなどが渡されていること

page1やpage2を足したのでhandlerをそれぞれ命名規約通り(getPage1R、getPage2R)を定義する。

Page1Rとかは

renderRoute HomeR  = ([], [])
renderRoute Page1R = (["page1"], [])
renderRoute Page2R = (["page2"], [])

こんな感じで内部的には処理されているらしいので、所詮ただのタプルである(第一引数はpath、第二引数はquery)
これをaタグのリンクにいれられるため、URLの変更に強かったりする。
確かrailsでもActiveRecordのmodelをa_tagに渡すことができたような気がするがそれに近いのかもしれん。


sample3 json

{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE QuasiQuotes          #-}
{-# LANGUAGE TemplateHaskell      #-}
{-# LANGUAGE TypeFamilies         #-}
import Yesod

data App = App

mkYesod "App" [parseRoutes|
/ HomeR GET
|]

instance Yesod App

getHomeR  = return $ object ["msg" .= "Hello World"]

main = warp 3000 App

これでjson responseが出来上がる。

{ msg: "Hello World" }

これはどの言語で書いてもこんなもんか。

今日はこんなところで。

では良いインプットと良いプログラミングを。

0 件のコメント:

コメントを投稿