みどりねこ日記

よくわからないけど、頑張りますよ。

Haskellモナドトランスフォーマー(8)

次はリストトランスフォーマーを考えましょう。 Maybeトランスフォーマーのように、1つ引数をとるコンストラクタ付きのデータ型を定義します。 newtype ListT m a = ListT { runListT :: m [a] } ListTモナドの実装も、もとのListモナドのそれと非常に似通…

Haskellモナドトランスフォーマー(7)

さて、前回の記事では>>=演算子を実装しました。しかし、実際のところ、何が起きているのでしょうか。 (>>=)演算子は、サンドイッチの層を一枚ずつ剥がし、中の値に関数を適用し、その結果である新たな値を新しいサンドイッチの中に詰めて返すのだと考えても…

Haskellモナドトランスフォーマー(6)

Maybeトランスフォーマーのデータ型を宣言するところから始めましょう。MaybeTコンストラクタはひとつ引数を取ります。トランスフォーマーは、元のモナドと同じデータを取るので、newtypeキーワードを使用することにします。dataキーワードを使ってもいいの…

Haskellモナドトランスフォーマー(5)

モナドトランスフォーマーがいかにして動いているのかは、どのように>>=演算子が実装されているかを理解するとわかるでしょう。>>=演算子の実装は、トランスフォーマーではない、もとのモナドのそれと非常に似ていることがわかると思います。トランスフォー…

Haskellモナドトランスフォーマー(4)

モナドトランスフォーマーはモナドの結合を簡単にするモナドの、特別な種類です。例えば、ReaderT Env IO aはEnv型の環境から読み取り、IOを行い、型aを返すことができる計算です。モナドトランスフォーマーの型コンストラクタはモナドのそれにパラメータ化…

Haskellモナドトランスフォーマー(3)

さて、準備が整ったので、getPasswordやそれを利用するコードを書きなおしてみましょう。 getValidPassword :: MaybeT IO String getValidPassword = do s <- lift getLine guard (isValid s) return s askPassword :: MaybeT IO () askPassword = do lift $…

Haskellモナドトランスフォーマー(2)

関数getPasswordとそれを利用するコードを簡潔に書くために、IOモナドにMaybeモナドの特性を与えるモナドトランスフォーマーを定義します。これを、モナドトランスフォーマーの慣例的な名付け方に従い、最初に与える特性を持つモナドの名前+Tとし、MaybeTと…

Haskellモナドトランスフォーマー(1)

ここまでで、モナドがどういうもので、IOモナドは不純な計算のため、Maybeは失敗するかもしれない計算のために、…というように、どのように使われるのかが理解できたのではないかと思います。実際の作業の中で、複数のモナドの特性を扱いたい場合があります…

Haskellのモナド3(5)

モノイドについて知らないなら、この記事がわからなくても問題無いです。モノイドは、幾つかの公理を満たす、単位元(ゼロ)と2項演算(プラス)の2つのメソッドがあるクラスです。 class Monoid m where mempty :: m mappend :: m -> m -> m 例えば、リス…

Haskellのモナド3(4)

mplusとmzeroの他にも、いくつか知っておくべき関数があります。msum MonadPlusのインスタンスを扱っている時に、モナドのリスト[Maybe a]、つまり[[a]]を受け取ってそれをmplusで折りたたんでいくという作業をよく行います。msumがまさにそれです。 msum ::…

Haskellのモナド3(3)

MonadPlusのインスタンスは、モナドのインスタンスがモナド則を満たす必要があるのと同様に、いくつかの法則を満たさなくてはなりません。残念ながら、これらの法則はまだ確定していません。ただ、確実に必要なのが、mzeroとmplusがモノイドを形成することで…

Haskellのモナド3(2)

入力をパースする伝統的な方法として一つづつ文字を消費する関数を書くという方法があります。すなわち、文字列をとって、それを切り離して、もしそれの先頭が予め決めた条件(例えば、大文字しか受け付けないという条件)を満たすならそれらを順次消費して…

Haskellのモナド3(1)

モナドについて学ぶうちに気づかれたかもしれませんが、どちらも計算結果の個数を表現するという意味でMaybeモナドとリストモナドは結構似ています。 すなわち、Maybeモナドを使用して計算が失敗する可能性(0個または1個の結果)を、リストモナドを使用し…

Haskellのモナド2(6)

最初の方に、return xは”なにもせずに”ただxを返すのだと説明しました。この考えは、Stateモナドのように副作用を起こすモナドにおいて使うとき、初めて色々な意味を持ちます。すなわち、Stateモナドの計算は内部状態を書き換えることでその後の計算の結果に…

Haskellのモナド2(5)

Stateモナドはコンテナという見方より、計算と見たほうがいいと思います。Stateモナドは「内部の値に依存したり、書き換えたりする計算」を表しています。例えば、3体問題(互いに相互作用する3体以上からなる系を扱う問題)のモデリングをしているとしま…

Haskellのモナド2(4)

Haskellのリスト内包表記と、リストモナドの形が似ているのは興味深いですね。例えば、下のコードは三平方の定理を満たすものをリストアップする関数です。 pythags = [(x, y, z) | z <- [1..], x <- [1..z], y <- [x..z], x^2 + y^2 == z^2] これをリストモ…

Haskellのモナド2(3)

リストモナドの計算(型[a]で終わるもの)は「0個以上の値が結果となる計算」を表しています。例えば、三目並べをモデリングしているとします。ちょっとだけわざとらしいのですが、ここではゲームが進行する可能性のある状態を見つけることを問題とします。…

Haskellのモナド2(2)

Maybeモナドの計算(Maybeモナドで包まれたものが結果となる関数呼び出し)は、「失敗するかもしれない計算」を表現しています。これの良い例として、データベースのルックアップテーブルから条件にマッチするものを引っ張ってくる処理があります。ルックア…

Haskellのモナド2(1)

前回まではモナドをコンテナの表現方法として扱って来ました。すなわち、モナドを構造的に見てきたということです。しかし「なぜ」モナドを使うのかは完全には説明していません。なんといっても、モナドの構造はとてつもなくシンプルですので、そもそもなぜ…

Haskellのdo記法(4)

do記法の最後の式はdoブロックの結果となります。今までの例では、結果の型はIO ()、つまりIOモナドの空の値でした。前回の例を、受け取った名前を結果として返す関数、つまり型としてはIO Stringである関数として書き換えます。することといえば、最後にret…

Haskellのdo記法(3)

ユーザに氏名を尋ねるプログラムを考えましょう。 nameDo :: IO () nameDo = do putStr "What is your first name? " first <- getLine putStr "And your last name? " last <- getLine let full = first ++ " " ++ last putStrLn ("Pleased to meet you, " …

Haskellのdo記法(2)

do記法に変換するとき、下流に値を渡していく(>>=)演算子は何も渡さない(>>)演算子に比べてちょっとだけ難しいです。これらの値は do result <- action another_result <- another_action (action_based_on_previous_results result another_result) これを…

Haskellのdo記法(1)

do記法はモナディックなコードを書くための別の記法です。特にIOモナドのような、仕様として純粋な値を取り出すことが出来ないモナドを使った処理を書くのには便利です。対照的にMaybeやリストのようなモナドからは純粋な値をパターンマッチやそのための関数…

Haskellのモナド(11)

モナドは数学の分野の一つである圏論から来ました。嬉しいことに、Haskellでモナドを理解して利用するために、圏論を理解する必要は全くありません。しかし、圏論でのモナドの定義はHaskellのそれと少し違います。Haskellに持ってきた時に、圏論のそれと同じ…

Haskellのモナド(10)

結合性のおかげで、仮にネストしたとしても>>=演算子が計算の順番のみに対して影響することが保証されます。例えば、下のコードも等価であることが保証されています。 bothGrandfathers p = (father p >>= father) >>= (\gf -> (mother p >>= father) >>= (\…

Haskellのモナド(9)

returnの振る舞いは、右結合、左結合を満たすことという指定があります。また、returnは値をとるだけで、実際にはなんの計算もしない、と決まっています。例として、 maternalGrandfather p = do { m <- mother p; gm <- father m; return gm; } は以下のコ…

Haskellのモナド(8)

命令型言語のようにブロックを組み立てたいとしても、>>=やreturnにむちゃくちゃな機能をつけるわけにはいきません。以下の3つに従う必要があります。 m >>= return = m return x >>= f = f x (m >>= f) >>= g = m >>= (¥x -> f x >>= g) これがモナド則と…

Haskellのモナド(7)

前回までの説明で、おそらく>>=演算子とreturnが、Maybeを使ったコードを書くときに、煩雑なコードから解放されるために有益であると同意してもらえると思います。これがどうして動くのか、そしてこれが何を意味しているのかを考えましょう。その説明のため…

Haskellのモナド(6)

Functorは関数fmapを適用できる型です。fmapは、リストでいうmapと同じようなものです。圏論によると、すべてのモナドは定義上Functorでもあります。しかし、GHC(事実上のHaskell標準処理系)はそれらを別として捉えており、MonadクラスはFunctorクラスとな…

Haskellのモナド(5)

Haskellでは、型クラスであるモナドはモナドを表すものとして使われています。モナドはControl.MonadモジュールとPreludeの中で定義されています。 class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b fa…