りんごがでている

何か役に立つことを書きます

Yesodチュートリアルの蛇足(2)

前回の続きを。

参考にしているチュートリアルはこちらから。
Haskell web programming

今日は2.Echoからやっていこう。

YesodはFront Controller Patternをとっている。
つまりYesodへのリクエストはひとつの入り口から入って、そこから各所に割り振られる。
その設定は、config/routesファイルに書かれている。

その中身はこんな感じ。(最後の行はチュートリアルの中で加えたルーティングのための宣言)

/static StaticR Static getStatic
/auth   AuthR   Auth   getAuth

/favicon.ico FaviconR GET
/robots.txt RobotsR GET

/ RootR GET

/echo/#String EchoR GET

最後の一行を加えて保存した瞬間、自動的にコンパイルがスタート。ォオー!!(゚д゚屮)屮

そして怒られる。

Application.hs:31:1: Not in scope: `getEchoR'

なるほど、getEchoRなんか知らんと。これはHandler/Root.hsの方に実装するらしい。
チュートリアルに従ってgetEchoRを加えて保存するとちゃんと動くようになった。

ところでこのEchoプログラム、XSSなどの対策は大丈夫なのだろうか??

それが大丈夫らしい。

詳しくはここに書いてあるが、Haskellの強力な型が我々を不正な入力から守ってくれる。
入力されたStringやText型の文章は、テンプレートの所定の場所に挿入される際、Html型に型変換される。
Html型はエスケープされているので、javascriptなどのコードを実行することはできなくなる。
かくして悪意のある入力から守られる。

Yesodで使われているテンプレートの形式は、シェイクスピアにちなんだ名前が付けられている。
HTML,CSS,Javascriptのテンプレートはそれぞれ以下のような名前が付いている。

HTML Hamlet
CSS Lucius
Javascript Julius

templateディレクトリにあるファイルの拡張子をみると、.hamletとか.luciusとか.juliusとかついていて、それぞれがHTML,CSS,Javascriptのテンプレートに対応しているらしい。

詳しい文法などについて勉強したら、またそのうち書きますm(_ _)m

Yesodチュートリアルの蛇足(1)

YesodというHaskell製のフレームワークがある。
近々O'ReillyからYesod本(Haskell and Yesod)が出るらしいので、その前にちょっと触ってみようと思ったのでその記録を。

参考にしたチュートリアルは、
Haskell web programming
冒頭の紹介によると、膨大なリクエストをさばく能力はnode.jsなんかに比べても圧倒的に性能がいいらしい。
YesodでEchoサーバからデータベースを使ったBlogアプリケーションまで説明している。
このチュートリアルに沿ってやっていこう。何回かに分けてポストするかも!

前準備

この2つがとりあえず必要。ここから手に入るのでインストール。
私が試したバージョンはGHCが7.0.4 Haskell Platformが4.0.0だった。

パッケージのインストールに必要なので、cabalが使えるようにパスを通そう。
デフォルトでは$HOME/.cabal/binにある様子。

% export PATH=~/.cabal/bin:$PATH

そして、Yesodのインストール。
これもcabalを通して行う。

% cabal update
% cabal install yesod cabal-dev

プロジェクトを作成

% yesod init

で対話的にプロジェクトの初期設定が可能。
名前とか、Foundationとか、何のデータベースを使うかとかを聞いてくる。
選択肢にあったDBは、

今回はサンプルだし依存関係が面倒でないSQLiteを使う。

つぎにプロジェクトのコンパイルをする
cabal-devは開発用に今のプロジェクトに限定してツールをビルドしてくれるそう。
yesodは--devオプションをつければ開発用サーバが立ち上がる。

% cabal-dev install && yesod --dev devel

が、このときlanguage-javascriptパッケージが正しくビルドできない問題が発生。
どうやらlanguage-javascriptはalexというパッケージのバージョンが3.0以上じゃないとダメらしい。
alexのバージョンが2.4だったので、アップデート。これもcabalから。

% cabal install alex

現在のcabalではupgrade機能が使えないようなので、installをした。
これでコンパイルができる。結構時間かかるし、特に終わっても何も出ないのでアレ?って感じになるが、
http://localhost:3000
にアクセスすると、デフォルトのメッセージが見られる。
f:id:bicycle1885:20120208043819p:plain
こんな感じ。

ふう、眠いのでまた次回。

スタートHaskell 第6回(最終回)に参加してきた

先週の土曜日に当たる1月28日、神保町近くにあるIIJを会場にした「スタートHaskell 第6回」に参加してきた。
スタートHaskellは去年7月の第0回から数えて7回目で、自分はこのうち6回参加したので結構参加者の人たちを(一方的に)覚えてしまった。

@koieさんが今回のまとめ記事を書いているようなのでリンク貼っとこ。

今回でテキストにしていた『プログラミングHaskell』はひと通り読み終わり、さて次は何をやろうか?と次の勉強会に向けて案を練っている様子でした。
私もほぼ第0回と同時にHaskellを勉強し始め、約半年ほどですっかりHaskellの魅力に取り憑かれてしまった。
Haskell恐るべし。

今回は最終回ということもあって、Haskellのスゴイ人がたくさん参加されていた。
ミーハーなのでちょっと感動。



スタートHaskell、参加してよかった。
この勉強会が始めて通して参加した勉強会だったので、特に得るものが大きかった気がする。
大学にいて、学部の専攻もほとんどコンピュータサイエンスと関係の無い分野にいると、なかなか勉強のモチベーションを保つのが難しい。
勉強会というのは通して参加することによってある程度のレベルまで自動的に引き上げられるし、何より本やブログより最新の話題の実感が湧きやすい。
特にHaskellのような比較的新しく研究と進化の活発な言語では、こういうイキイキとした情報を得られるのはとても有意義なことだ。

  • 「コレに関してはこの資料が詳しい」
  • 「この人のブログを読んだらいい」
  • 「最近はこういうスタイルが流行っている」
  • 「このへんはもう主流じゃない」
  • 「こういうツールがあってこんなふうに使える」

まさにこんな情報が欲しいのだ!
Haskellerたちのブログやツイッターをチェックして動向を追えばメインストリートが見えてくる。
そういうキュレーターを通して情報を得れば、とっても効率がいい。
私みたいな学生には特に技術系の勉強会に参加して欲しい。エンジニアリングの現場の人の話が聞ける最高の機会になるだろう。

こんなのが買えるのも、勉強会の特典?
簡約!? λカ娘(二期) - 参照透明な海を守る会
f:id:bicycle1885:20120131011248j:plain

一体何番煎じか分かったもんじゃないけどMonadでStack (2)

前回の記事で作製した

data Stack a = Stack a (Stack a) | Empty deriving (Show)
type MonadStack a = State (Stack a) a

で問題が発覚したから、ちゃんと直しましょう。名だたるHaskeller達に噴飯されないように。せめて失笑レベルまで持って行きたいな。


型変数追加

で何が問題かって言うと、MonadStackの型変数がaだけでStackに押し込む値の型も、MonadStackの操作で返る値の型も同じaだってこと。
じゃ、それぞれに別々の型変数を用意すればイイね。ってことで型変数bをMonadStackに追加で。

data Stack a = Stack a (Stack a) | Empty deriving (Show)
type MonadStack a b = State (Stack a) b

これでOK。あとは実装した関数たちの型も変更してやれば大丈夫ですな(ノ´∀`*)

push :: a -> MonadStack a a
pop :: MonadStack a a
top :: MonadStack a a
empty :: MonadStack a Bool
size :: MonadStack a Int

(*実装省略)


> runState (push 1 >> empty) Empty
(False,Stack 1 Empty)
> runState (empty) Empty
(True,Empty)

やったぜワッショイ(∩´∀`)∩ワーイ


ついでにfoldとfmapも…

Stackに対するfoldrとfmapも定義してみよう。

import Prelude hiding (foldr)

instance Foldable Stack where
    foldr f z Empty = z
    foldr f z (Stack x stack) = foldr f (f x z) stack

instance Functor Stack where
    fmap f Empty = Empty
    fmap f (Stack x stack) = Stack (f x) (fmap f stack)


よし。そしてMonadStack用にmapSとfoldSも作ったぞ。役に立つかは知りません。

mapS :: (a -> b) -> MonadStack a (Stack b)
mapS f = do
    stack <- get
    return $ fmap f stack

foldS :: (a -> b -> b) -> b -> MonadStack a b
foldS f x = do
    stack <- get
    return $ foldr f x stack

とまぁこんな感じでStackをMonadで扱えるようにしてみた。
せっかくだから何かに使いたいな〜なんて思ったりしている(´・ω・`)

一体何番煎じか分かったもんじゃないけどMonadでStack (1)

前回のスタートHaskell第5回に触発されたので色々とモナドをいじっていこうっと♪~(´ε` )
おっと、触発されただけで別にマスターしてないので悪しからず。
まだ全然Monad理解してないのでツッコミなどあったら是非是非お願いします。

さて、状態を扱えるControl.Monad.Stateを使って基本的なデータ構造であるStackを実装してみる。
ご存知のとおりHaskellには状態という概念がないので、Monadを使ってシミュレートするわけだ。


とりま、型だけ書いとけ

今回実装する関数は、C++のSTLにあるstackを参考にpush,pop,top,size,emptyの5種類
とりあえずまぁ、Stackを扱うState Monadの型をかいてみよう。

module Stack where

import Control.Monad.State (State, get, put, runState)

data Stack a = Stack a (Stack a) | Empty deriving (Show)
type MonadStack a = State (Stack a) a

push :: a -> MonadStack a
push = undefined

pop :: MonadStack a
pop = undefined

top :: MonadStack a
top = undefined

empty :: MonadStack Bool
epmty = undefined

size :: MonadStack Int
size = undefined

うん、いいんじゃない?コンパイラに怒られないし。


個々の関数の実装

では、関数行ってみよう
ちなみにgetは今の状態を結果として返してくれる関数で、putは引数を状態に設定して何も返さない関数ですよ。

push
push :: a -> MonadStack a
push x = do
    stack <- get
    put $ Stack x stack
    return x

今の状態をgetでstackに束縛し、putで新しくxを追加したStackを状態として設定し、何となく引数をそのまま返しています。
最後はreturn ()のほうがいいですかね。


pop
pop :: MonadStack a
pop = do
    Stack x stack <- get
    put stack
    return x

getで返された値をStack x stackでパターンマッチしてstackをputし、xを返しています。


top
top :: MonadStack a
top = do
    Stack x _ <- get
    return x

上のpopのstackをputしないバージョンです。


size
size :: MonadStack Int
size = get >>= (\stack -> return (size' 0 stack))
    where
        size' x Empty = x
        size' x (Stack _ stack) = size' (x+1) stack

do記法を使わずに書いてみました。sizeは毎回計算しています。


empty
empty :: MonadStack Bool
empty = get >>= (\stack -> empty' stack)
    where
        empty' Empty = return True
        empty' (Stack _ _) = return False

この辺も見たまんまです。2行目は

empty = get >>= empty'

のほうが単純だけど、わかりやすさのために上のような感じで書いてみました。


試してみよう
> runState (push 1 >> push 2 >> push 3 >> pop) Empty
(3,Stack 2 (Stack 1 Empty))
> runState (push 1 >> top) Empty
(1,Stack 1 Empty)
> runState (push 1 >> push 1 >> size) Empty
(2,Stack 1 (Stack 1 Empty))

うん、いいかんじ。


しかし問題が発生

お気づきになっただろうか?( ゚д゚)気づいた人はもういいです。
今までのコード、別にGHCにもぐもぐさせてコンパイルエラーはでないんだけど、すでに重大な問題がある。

例えば次の例

> runState empty Empty
(True,Empty)

はいいんだけど、これはダメ

> runState empty (Stack 1 Empty)

<interactive>:1:23:
    No instance for (Num Bool)
      arising from the literal `1'
    Possible fix: add an instance declaration for (Num Bool)
    In the first argument of `Stack', namely `1'
    In the second argument of `runState', namely `(Stack 1 Empty)'
    In the expression: runState empty (Stack 1 Empty)

あぁん!?( ゚д゚)


これ実はMonadStackのデータ型に問題があった。

type MonadStack a = State (Stack a) a

よく見るとStackの型変数とStateの二つめの型変数が同じaを使ってるじゃん。
これじゃぁStackに押しこむ要素とStackの操作関数の返り値が同じ型じゃないとダメということになる。
popやtopなんかはStackの要素と返り値が同じ型だから問題ないけど、emptyは返り値がBoolだからStack BoolのStackにしか使えない。
だからこれはOKだけど上のはダメと。

> runState (push True >> push False) Empty
(False,Stack False (Stack True Empty))


なるほどなるほど。さっきsize関数がOKだったのもStackの要素と返り値が偶然Intで一致したからなんだね。
だからこれはダメ。

> runState (push True >> push False >> size) Empty

<interactive>:1:38:
    Couldn't match expected type `Bool' with actual type `Int'
    Expected type: Control.Monad.Trans.State.Lazy.StateT
                     (Stack Bool) Data.Functor.Identity.Identity a0
      Actual type: MonadStack Int
    In the second argument of `(>>)', namely `size'
    In the first argument of `runState', namely
      `(push True >> push False >> size)'

ちょっと分かってきたところで修正したモノはまた次回でヽ(´ー`)ノ

スタート Haskell 第5回

スタートHaskell第5回(http://atnd.org/events/22289)参加してきましたのでメモなどを。


概要

発表

今回はプログラミングHaskellの10章と11章のお話+山本和彦(@kazu_yamamoto)さんのモナドチュートリアル

その後

後半の実習と懇談会は予定があって参加できなかったけれども次回は是非参加したい。


詳細

10章 型とクラス

@ruiccさんの発表 http://www.slideshare.net/RuiccRail/programming-haskell-chapter10
型とクラスに関して、オブジェクト指向Javaと関数型のHaskellの違いや類似点を見ていこうというお話
Haskellの型の分類

  • 直和構造
    • タプル
    • ベクトル/行列
  • 直積構造
    • Bool
  • 再帰構造
    • Tree
    • List

なるほどなるほど。
こういった型はやはりHaskellの方が簡潔な表記で作りやすい印象
ユーザー定義の型と組み込みが区別なく使えるので便利ですね

その他、(多重)継承など
この辺でなかなか議論がカオスになって行く
Haskellのclassとは何なのか?
Twitter上ではJavaのインターフェースに対応するという認識が一般的
Haskellのclassって幾つかの型に対して何か共通の性質を持たせたい時の制約
と考えてたら良いんでないかね。なかなか類推が難しい領域

そのへんオブジェクト指向と関数型の型の世界に関しては
「記号と再帰」(著:田中久美子)とか参考に考えていきたい

11章 切符番号遊び

@dekosukeさんの発表 http://www.slideshare.net/dekosuke/programming-haskell-chapter-11
なかなか奥深い話だからプログラミングHaskellを読み合わせて理解していきたい
11章サボっててすいません(´;ω;`)

モナドチュートリアル

@kazu_yamamotoaさんの発表 http://mew.org/~kazu/monad.pdf
題して「モナモナ言わないモナド入門」
前回までのスタートHaskellでは名前さえ言ってはいけなかった "Monad" さま
そもそもなぜモナドを理解出来ないのかというお話から

だって、モナド抽象的だから
モナドが理解出来ないのはそもそもモナドが抽象的な概念であって、直接的にその概念をうかがい知ることはできないよね

大統一理論のアナロジー」:
大統一理論は4つの具体的な力から重力を除いたものを抽象しようとするもの
それ単独で必要性や概念を理解することは難しい
モナドも同様に幾つかの具体的な概念を抽象する概念なのでそのまま理解することは難しい

だから具体例から見てみようという流れ

資料を見たほうがよく分かるかと

  • 状態系
    • Parser
    • IO
  • 失敗系
    • List
    • Maybe
  • その他いろいろ

これらをすべて抽象するんだからモナドはとっても抽象的なのはわかる
あとは色々と具体例

そして後半はモナドに関する力の階層の問題

強い順に

  • プログラミング可能コンテナ Monad (>>=)
  • 逐次コンテナ Applicative (<*>, return)
  • マップ可能コンテナ Functor (<$>)

そして強いものは可能な限り使用を避ける方がいいので
do記法は避けていこうというお話

そうして「抽象の壁」を突破し、僕らはモナドマスターとなったのであった(え?)

今後モナドをガンガン使って行きましょうね