この記事は、Haskell Advent Calendar 2014 23日目の記事です。
僕自身が、駆け出しHaskellerなのであまり難しいことは書けません。きっと中級以降の人には常識的な話題で「何を今更・・・何周遅れだよ(´・ω・`)」みたいな微妙な話ですが、お付き合いください。
しかし、Haskellと言うとモナドみたいな風潮は何とかならんのか・・・。
なりません!(`・ω・´)シャキーン
モナドにも種類がある
ということで、モナドの話です。
モナドというのは、Monadという型クラスです。型クラスというのJavaで言うとインターフェースのようなものです。
これを実装した上で、モナド則と言われる規則をクリアし、厳しい試練に耐えぬいた型だけがMonadになれます。大変ですね。
例えば、こんな型がモナドとして知られています。
- Maybe
- []
- Identity
- Either
- State
- IO
- Writer
- Reader
- Parser
一口にモナドと言っても、インターフェースなので、実装される型の性質によって、振る舞いが違います。
まず、モナドというと真っ先に晒し者にされるMaybeですが、成功と失敗を表す型で、定義は下記のような感じです。
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
簡単ですね。
これに対して、Stateモナドは、こんな感じです。
instance Monad (State s) where
return x = State $ \s -> (x, s)
(State h) >>= f = State $ \s -> let (a, newState) = h s
(State g) = f a
in g newState
複雑ですね。
これらのモナドインスタンスを一緒くたにして「モナドとは!」とかやり始めると、ギャップにびっくりした初心者が死ぬわけです。(´・ω・`)ムリポ
Stateモナドは複雑組のリーダー
Haskellを学習していると、色々なモナドが出ていくるのですが、自分の中では「簡単組」と「複雑組」に分かれていると思っています。
最初に出会った複雑組が、IOモナドだったこともあり、当初「IOこそがモナドのボス!」と思っていた時期が僕にもあったのですが、色々見ていくうちに複雑組の典型例はこの「State」なんじゃないか。
と思えるようになってきました*1。
それは何故かと言うと、上記の実装パターン「代数的データ型の中に関数が入っていること」「その関数を紡ぎ合わせて全体を一つの関数にまとめ上げること」という形が出現し、IOでは処理系の裏に隠されている代数データ型の中の関数を自分で定義できるという点が、代表者として相応しい風格を放っているからです。
代数データ型の中に関数が入っている
State型の定義は下記のような感じになります。
newtype State s a = State { runState :: s -> (a, s) }
代数データ型の中に関数が入っているとは、s -> (a, s)という関数をStateで包んでいる事を指しています。
このパターンの場合、関数の形は「何か(s)を受け取って、何か(s)と別の何か(a)のペアを返す」という形式になっています。
渡される何か(s)と帰ってくる何か(s)は、型は同じですが、状態は変化します。例えば、1を渡すと2が返ってくるような感じです。
代数データ型の中の関数を紡ぎ合わせる
実装を追っていけは、そのまんまの意味なのですが、初心者殺しだと思いますので、見えるように視覚化したものを参考までに。手前味噌ですが。
モナドってなんだよ!?全然わからないんで分解して図解してみた(´・ω・`) - セカイノカタチ
言葉にするのが難しいですが、バインド(>>=)関数の第一引数を前として、戻り値を後とすると「前 >>= f :: 後」となりますが(fはココでは無視)、「前から来たデータ型を剥いて関数を取り出し、戻り値に包む関数の 引数をそのまま渡す」。という動きをしています。後ろから来た引数を手前にバトンリレーしていくわけです。
「関数を受け取り関数を返す関数」という関数がゲシュタルト崩壊しそうな関数をこともなげに使いこなすのが修羅の国の名も無きHaskellerなので気をつけて下さい。ファルコも真っ青です*2。
なので、「あ…ありのまま 今 起こった事を話すぜ!『おれは奴の前で、関数を戻したと思ったら関数を呼ばれていた』」状態であり、「一体いつから関数を呼んでいると錯覚していた?」状態なわけです。もーわけわかめ。(´・ω・`)
IOモナドはStateの特殊形
IOの定義は、処理系実装の裏側に隠されているので、確かなことは言えないのですが、イメージで言うと下記のような形になっているそうです。
type IO a = World -> (a, World)
Stateと同じく、s -> (a,s)という形になっていますね。Stateの場合、パタンマッチなどを使えば、データ型に包まれた中身の関数にアクセスできますが、IOの場合はそこには触れられないので、あくまで想像するしかありません。
(runIO $ print x) world
みたいに取り出すことができないのです*3。
この点が、IOよりもStateの方が複雑組のモナドを勉強するのに適している理由です。
やはり、実装の中身が見えていて、自分でも実装できる方が実感が湧いて理解するのが早くなりますよね。
モナドについて覚えなければいけないのは2パターン
ということで、モナドについて覚えなければいけないのは「単純なパターン」と「関数を包むStateモナドパターン」の2つということになります。
Maybeなどの単純なパターンで、モナドの基本的な機能や概念の学習を進め、十分理解が進んだと思ったら、Stateモナドを学んで、複雑組のパターンに慣れる。という道筋がいいんじゃないでしょうか。
おまけ
今回のコード片は、「すごいHaskellたのしく学ぼう!」から大体引用しました。
現在、自分が主催で「第2期 第4回 H本読書会 in 秋葉原 - H本読書会(Haskell勉強会) | Doorkeeper」という勉強会をやっていますので、よろしかったら足を運んで一緒に学びましょう!
大丈夫!まだ第3章が終わったところなので、モナドのモの字も出てきません!(^^;
しばらくすると、Stateモナドもやるはずなので、今から参加したらいいと思います。(^^;;;