この記事について この記事は、 Erik さんのを翻訳したものです。 導入 Haskell について知っていればいるほど良いですが、あなたが熱心な Haskeller であるならこの記事は少々退屈に感じるでしょう。もし Haskell の基本について学びたいのであれば、Learn …
引き続き、Magnus氏のブログの翻訳です。 前回の記事に対して、Conal Elliottから興味深いコメントを頂きました。 もっと関数的でApplicativeな書き方をしてもいいかもね。 doで繋がったParsecコードをliftM, liftM2, ...といったように置換してみよう。読ん…
Magnusさんのご厚意により、邦訳してもよいことになりましたので掲載させて頂きます。 もとの記事はこちらのAdventures in parsingです。 私はParsecが使いこなせるようになりたいと常日頃から思っていました。何度か試してみたのですが、そのいずれもどこか…
面白い記事があったので翻訳してみました。 ライセンスはCreative Commonsです。 しばらくScalaで仕事をして、疑う余地なく以下のことが断言できるようになりました。”Scalaは関数型言語ではありません。クロージャを持ち、静的な型を持つオブジェクト指向言…
今まで、MaybeTとListTという2つの非常に単純なモナドトランスフォーマーの実装をしてきました。それから、少し遠回りをしてモナドをそれのトランスフォーマーに持ちあげるという話もしましたね。ここでは、その二つの考えを、StateTという標準ライブラリの…
liftの実装はわりと素直です。MaybeTトランスフォーマーの場合を見てみます。 instance MonadTrans MaybeT where lift mon = MaybeT (mon >>= return . Just) 内側のモナドを値に適用し、サンドイッチで言う真ん中までの処理を行ったあと、>>=演算子と型コン…
モナドトランスフォーマーによって合成されたモナドを使うとき、内側のモナドをきちんと考える必要がないようにしたいところです。そのほうが綺麗でシンプルなコードになるからです。内側のモナドの型を持った値を扱う計算の中でdoブロックを使うより、内側…
標準ライブラリの中でも、とりわけ使いやすい関数の一つに、liftMがあります。モナディックでない関数をモナドの関数に持ち上げます。型を見てみましょう。 liftM :: Monad m => (a1 -> r) -> m a1 -> m r さて、liftMは関数(a1 -> r)と、a1を保持したモナド…
次はリストトランスフォーマーを考えましょう。 Maybeトランスフォーマーのように、1つ引数をとるコンストラクタ付きのデータ型を定義します。 newtype ListT m a = ListT { runListT :: m [a] } ListTモナドの実装も、もとのListモナドのそれと非常に似通…
さて、前回の記事では>>=演算子を実装しました。しかし、実際のところ、何が起きているのでしょうか。 (>>=)演算子は、サンドイッチの層を一枚ずつ剥がし、中の値に関数を適用し、その結果である新たな値を新しいサンドイッチの中に詰めて返すのだと考えても…
Maybeトランスフォーマーのデータ型を宣言するところから始めましょう。MaybeTコンストラクタはひとつ引数を取ります。トランスフォーマーは、元のモナドと同じデータを取るので、newtypeキーワードを使用することにします。dataキーワードを使ってもいいの…
モナドトランスフォーマーがいかにして動いているのかは、どのように>>=演算子が実装されているかを理解するとわかるでしょう。>>=演算子の実装は、トランスフォーマーではない、もとのモナドのそれと非常に似ていることがわかると思います。トランスフォー…
モナドトランスフォーマーはモナドの結合を簡単にするモナドの、特別な種類です。例えば、ReaderT Env IO aはEnv型の環境から読み取り、IOを行い、型aを返すことができる計算です。モナドトランスフォーマーの型コンストラクタはモナドのそれにパラメータ化…
さて、準備が整ったので、getPasswordやそれを利用するコードを書きなおしてみましょう。 getValidPassword :: MaybeT IO String getValidPassword = do s <- lift getLine guard (isValid s) return s askPassword :: MaybeT IO () askPassword = do lift $…
関数getPasswordとそれを利用するコードを簡潔に書くために、IOモナドにMaybeモナドの特性を与えるモナドトランスフォーマーを定義します。これを、モナドトランスフォーマーの慣例的な名付け方に従い、最初に与える特性を持つモナドの名前+Tとし、MaybeTと…
ここまでで、モナドがどういうもので、IOモナドは不純な計算のため、Maybeは失敗するかもしれない計算のために、…というように、どのように使われるのかが理解できたのではないかと思います。実際の作業の中で、複数のモナドの特性を扱いたい場合があります…
モノイドについて知らないなら、この記事がわからなくても問題無いです。モノイドは、幾つかの公理を満たす、単位元(ゼロ)と2項演算(プラス)の2つのメソッドがあるクラスです。 class Monoid m where mempty :: m mappend :: m -> m -> m 例えば、リス…
mplusとmzeroの他にも、いくつか知っておくべき関数があります。msum MonadPlusのインスタンスを扱っている時に、モナドのリスト[Maybe a]、つまり[[a]]を受け取ってそれをmplusで折りたたんでいくという作業をよく行います。msumがまさにそれです。 msum ::…
MonadPlusのインスタンスは、モナドのインスタンスがモナド則を満たす必要があるのと同様に、いくつかの法則を満たさなくてはなりません。残念ながら、これらの法則はまだ確定していません。ただ、確実に必要なのが、mzeroとmplusがモノイドを形成することで…
入力をパースする伝統的な方法として一つづつ文字を消費する関数を書くという方法があります。すなわち、文字列をとって、それを切り離して、もしそれの先頭が予め決めた条件(例えば、大文字しか受け付けないという条件)を満たすならそれらを順次消費して…
モナドについて学ぶうちに気づかれたかもしれませんが、どちらも計算結果の個数を表現するという意味でMaybeモナドとリストモナドは結構似ています。 すなわち、Maybeモナドを使用して計算が失敗する可能性(0個または1個の結果)を、リストモナドを使用し…
最初の方に、return xは”なにもせずに”ただxを返すのだと説明しました。この考えは、Stateモナドのように副作用を起こすモナドにおいて使うとき、初めて色々な意味を持ちます。すなわち、Stateモナドの計算は内部状態を書き換えることでその後の計算の結果に…
Stateモナドはコンテナという見方より、計算と見たほうがいいと思います。Stateモナドは「内部の値に依存したり、書き換えたりする計算」を表しています。例えば、3体問題(互いに相互作用する3体以上からなる系を扱う問題)のモデリングをしているとしま…
Haskellのリスト内包表記と、リストモナドの形が似ているのは興味深いですね。例えば、下のコードは三平方の定理を満たすものをリストアップする関数です。 pythags = [(x, y, z) | z <- [1..], x <- [1..z], y <- [x..z], x^2 + y^2 == z^2] これをリストモ…
リストモナドの計算(型[a]で終わるもの)は「0個以上の値が結果となる計算」を表しています。例えば、三目並べをモデリングしているとします。ちょっとだけわざとらしいのですが、ここではゲームが進行する可能性のある状態を見つけることを問題とします。…
Maybeモナドの計算(Maybeモナドで包まれたものが結果となる関数呼び出し)は、「失敗するかもしれない計算」を表現しています。これの良い例として、データベースのルックアップテーブルから条件にマッチするものを引っ張ってくる処理があります。ルックア…
前回まではモナドをコンテナの表現方法として扱って来ました。すなわち、モナドを構造的に見てきたということです。しかし「なぜ」モナドを使うのかは完全には説明していません。なんといっても、モナドの構造はとてつもなくシンプルですので、そもそもなぜ…
do記法の最後の式はdoブロックの結果となります。今までの例では、結果の型はIO ()、つまりIOモナドの空の値でした。前回の例を、受け取った名前を結果として返す関数、つまり型としてはIO Stringである関数として書き換えます。することといえば、最後にret…
ユーザに氏名を尋ねるプログラムを考えましょう。 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, " …
do記法に変換するとき、下流に値を渡していく(>>=)演算子は何も渡さない(>>)演算子に比べてちょっとだけ難しいです。これらの値は do result <- action another_result <- another_action (action_based_on_previous_results result another_result) これを…