Monad tutorial

79
函数プログラミングの集い2011 チュートリゕル 「モナドについて」 株式会社 Preffered Infrastructure 田中 英行 [email protected]

description

 

Transcript of Monad tutorial

Page 1: Monad tutorial

函数プログラミングの集い2011 チュートリゕル

「モナドについて」

株式会社 Preffered Infrastructure

田中 英行

[email protected]

Page 2: Monad tutorial

自己紹介

• 田中英行 (@tanakh, id:tanakh)

• 株式会社 Preferred Infrastructure (PFI) 勤務 –検索エンジンのゕルゴリズムとか作ってます

• Haskell (2004~)

• C++ (1998~)

• BASIC (1992~)

• プログラミングコンテスト愛好家 – ICPC, ICFPC, CodeJam, TopCoder, …

Page 3: Monad tutorial

本日の概要

• モナドとは?

• モナドの使い方

–モナドのセマンテゖクス

–典型的なモナドの例

– Haskellでのモナド

• モナドの作り方

–モナドのンスタンスに

–モナド変換子

Page 4: Monad tutorial

モナドとは? What is Monad?

Page 5: Monad tutorial

モナドとは何か?

• 非常に難しい質問です

• 既に数多くの人がそれに答えようとしています

–そして各々が一見全く違う主張をしている

Page 6: Monad tutorial

幾つかの例

Page 7: Monad tutorial

『モナドは象である』

Page 8: Monad tutorial

『モナドはメタフゔーではない』

Page 9: Monad tutorial

『プログラマブル・コンテナ』

Page 10: Monad tutorial

『コンベゕのゕナロジー』

from “All About Monads”

Page 11: Monad tutorial

いろいろありますが…

• 結局モナドとはなんなのか?

モナドの力の秘密、 いつか解き明かしてみたいな…

Page 12: Monad tutorial

モナドについての疑問

• 疑問にもいろいろある

–モナドとは何か?

–何の役に立つのか?

–なぜモナドなのか? モナドがもてはやされるのか?

• ゕローとモナド、どうして差がついた…

• それぞれについて、私なりの見解

Page 13: Monad tutorial

モナドとは何なのか?

• モナドとはパラダムである !

Page 14: Monad tutorial

抽象的な話

Page 15: Monad tutorial

パラダム?

• プログラミングパラダム

–手続き型

–関数型

–論理型

–オブジェクト志向

–モナデゖック(?)

• ここじゃない

Page 16: Monad tutorial

モナドとは手続き型パラダムの再定義である

• プログラミングパラダム

–手続き型

• モナデゖック

–関数型

–論理型

–オブジェクト志向

Page 17: Monad tutorial

意味的側面からの モナドのメタフゔー

• モナドは『プログラマブルセミコロン』である

Page 18: Monad tutorial

Programmable Semicolon

• Real World Haskell より

Page 19: Monad tutorial

手続き型言語と構造化定理

• プログラムは、「順次・反復・分岐」の基本的な構造の組み合わせによって記述できる

• これらはモナドへ直接的にマッピングできる

Page 20: Monad tutorial

それで、何が嬉しいの?

• 分岐のセマンテゖクス書き換え

• 反復のセマンテゖクス書き換え

–そういうことができる言語は過去にはあった

• 逐次のセマンテゖクス書き換え

–かつて無いもの(…の様な気がします)!

Page 21: Monad tutorial

逐次のセマンテゖクス

• およそほとんどの手続き型言語では、 セミコロンの意味は変えられない

–空気のような存在

• セミコロンの意味を変えることの意義が 伝統的に見逃されて来たのではないか?

int main() { foo(); bar(); }

main = foo >> bar

Page 22: Monad tutorial

継続との関係

• モナドは継続(continuation)の一般化とも考えることができます

• 継続=各セミコロンにおけるプログラムの状態

• モナドはセミコロンを記述するわけなので、その状態を取り出すことは簡単

–実際に継続モナドというものがあります

Page 23: Monad tutorial

セミコロンをいじることにより 可能になること

• 普通のプログラムを非決定計算に変える

• 普通のプログラムにエラーハンドリングを(プログラムを書き換えずに)追加する

• 普通のプログラムに暗黙の状態を導入する

Page 24: Monad tutorial

コンテクスト

• 具体的なモナドに対して、それが計算に付加価値を与えます。それを(計算の)コンテクストと呼ぶことにします –つまり、モナドというのはコンテクスト付きの計算ということができる

• 例えば… – monad:リストモナド → ctx:非決定性

– monad:Stateモナド → ctx:mutableな状態

– etc …

Page 25: Monad tutorial

それぞれにそういうプログラムを 書けばいいんじゃないんですか?

• コンテクストごとに異なる記法

• コンテクストごとに異なるプログラム

• コンテクストごとに異なる語彙

・・・

抽象化の欠如

Page 26: Monad tutorial

コンテクストの抽象化

• 共通のゕルゴリズムの記述

• 共通のコード片の抽象化

よりメタレベルの抽象化へ

Page 27: Monad tutorial

床下配線のゕナロジー

• モナドによるセミコロンの抽象化は 床下配線と例えられることも

–見えないところを書き換える

Page 28: Monad tutorial

少し具体的な話

Page 29: Monad tutorial

• N-クーン問題

– Int が与えられて解を返す

–解とはなんぞや?

• 全解列挙

• どれか一つを見つける

• 一番いい解を頼む

–いろいろ考えうる

Page 30: Monad tutorial

N-Queen問題

• モナデゖックに書くと、これらを統一的に扱える

–解の列挙のストラテジをモナドとして記述

–問題を解くゕルゴリズムからの分離

Page 31: Monad tutorial

前半まとめ

• モナドとは何なのか? –計算コンテクストの抽象化である

• なんでそれが嬉しいの? –具体的なコンテクストに依存しないコードを書ける

–抽象化したものを具体的なコードにできる

• なぜモナドなの? –モナドは構造化定理に必要な要素を自然に記述できて、なおかつ簡潔であるから

Page 32: Monad tutorial

三行で言うと

• モナドは関数レベルで

• メタプログラミング

• するためのものです

(´・_・`)えっ…?

Page 33: Monad tutorial

モナド入門・モナドの使い方 How to use Monads

Page 34: Monad tutorial

実装レベルのお話

Page 35: Monad tutorial

モナドとは

• “ある特定の方法”で組み合わせることのできる計算のことをまとめて、

と呼びます。

モナド

Page 36: Monad tutorial

ちなみに

• 組み合わせ可能な計算はモナドだけではありません – いろいろな抽象化におけるそれが存在

• 例えば、 – 関数 (関数合成)

– フゔンクタ (関手)

– ゕプリカテゖブ・フゔンクタ

– ゕロー

– Iteratee

– etc…

Page 37: Monad tutorial

ひとまず置いておいて、 Haskellでのモナドのお話

Page 38: Monad tutorial

Haskellでのモナド

• 次のような型クラス

これのンスタンスが具体的なモナド=計算コンテクスト

Page 39: Monad tutorial

モナドになっている標準データ型

• 標準データ型の中にもモナドがある

–リスト

– Maybe

– Either

– IO

Page 40: Monad tutorial

モナド則の必要性

• モナド則は、モナドを安全に組み合わせるのに必要

–これらの挙動が同じでなければ、組み合わせ方によって意味が変わるということになる

Page 41: Monad tutorial

Haskellのdo記法

• Haskellではモナドを非常によく使うので、専用の構文糖衣が用意されています

Page 42: Monad tutorial

高度なモナドの使い方

Page 43: Monad tutorial

リストモナド

• リストはモナドにできる

• リストは非決定 計算のコンテクスト と捉えることが できる

Page 44: Monad tutorial

Maybeモナド

• Maybeもモナド

• 失敗するかもしれない計算

Page 45: Monad tutorial

IOモナド

• 入出力を行う可能性のある計算

• HaskellではIOモナドを介してしか入出力を扱えない

Page 46: Monad tutorial

IOモナドの功罪

• モナドに関するよくある誤解

–モナドってpurely functional languageでIOするためにあるんでしょ?

• モナドはIOのためにあるのではありません

• モナドはIOのためにあるのではありません

–大事なことなので

Page 47: Monad tutorial

モナド則

• すべてのモナドは次のモナド則を満たす ”べき”である

–満たす保証をするのはプログラマの責任

–あえて満たさなくても良い

Page 48: Monad tutorial

モナド変換子(Monad Transformers)

• モナドとモナドを組み合わせるもの

– (cf. 計算と計算を組み合わせるものがモナド)

Page 49: Monad tutorial

動機

• 複数の計算コンテクストを合成したい

–失敗するかもしれないIO計算

–エラーハンドリングできるパーザー

– etc, …

Page 50: Monad tutorial

MTL (Monad Transformer Library)

• 標準のモナドラブラリ

– Preludeのモナドを大幅強化

• これらのものを含む

–幾つかの標準的なモナド

–これらのモナドを合成するための モナド変換子(Monad Transformers)

Page 51: Monad tutorial

MTLに含まれるモナド

• Monad.Cont (継続)

• Monad.Reader (ReadOnly状態)

• Monad.Writer (ログ出力)

• Monad.State (Mutable状態)

• Monad.List (非決定計算)

• Monad.Error (エラーハンドリング)

• これに加え、それぞれのモナド変換子版

Page 52: Monad tutorial

モナド変換子

• 2つのモナドを合成するためのもの

• 例えばStateモナドの場合:

– StateT s m a • モナド変換子版Stateモナド

• State s a と比べて、mというパラメータが追加

• mに合成したいモナドを代入

– StateT s IO a • IOモナドを内包したStateモナド

• IOモナドの操作とStateモナドの操作が両方できる

Page 53: Monad tutorial

持ち上げ(lift)

• StateT s IO の例

– StateT s IO の中でIOを行うには、持ち上げ(lift)を行う必要がある(型が合わないので)

Page 54: Monad tutorial

モナドクラス

• 例えばIOを行うだけの計算

• これは、StateT s IO 以外のモナドでも使えて欲しい

– ErrorT err IO

– ReaderT s IO

– …

Page 55: Monad tutorial

MonadIOクラス

• そのために、IOをliftできるクラス全体を抽象化したMonadIOクラスを定義

• 先のコードは次のような型に

• StateT s IO などをMonadIOのンスタンスにすれば使用可能に。

Page 56: Monad tutorial

一般の持ち上げ

• モナド変換子に渡されるモナドはIOだけではない –ネストする場合もある

• 一般ケースのために、MonadTransというクラスが用意されている

–内側のモナド外側のモナドに持ち上げることができる

Page 57: Monad tutorial

中盤まとめ

• モナドとは >>= と return の2つの演算が定義されたもの

• 標準データ型の多くのものがモナドになっている

• mtlというモナド変換子ラブラリがある

• モナド変換子を用いてモナドを組み合わせる

• モナド持ち上げで型の異なるモナドを張り合わせる

Page 58: Monad tutorial

Advanced Topics

Page 59: Monad tutorial

monad-control (1)

• liftの逆をするもの

• 動機

– Haskellの例外ハンドリング機構はIOモナドベース

–渡せるものがIO固定

• MonadIO に対しても例外ハンドルしたい

Page 60: Monad tutorial

monad-control (2)

• MonadIOに対して一般化

• それを行えるようにするために、MonadControlIOというクラスを用意

Page 61: Monad tutorial

モナドいろいろ

• 近年実に様々なラブラリがモナデゖックラブラリとして提供されるようになりました

• それらの一部を紹介していきたいと思います

Page 62: Monad tutorial

MonadPar

• 並列計算を記述するためのモナド

– `par`, `seq` などをモナド化したもの

Page 63: Monad tutorial

Parser

• パーザいろいろ

– Parsec, Attoparsec, trifecta

Page 64: Monad tutorial

WebApp

• WAI, Yesod, Snap, CGI, …

Page 65: Monad tutorial

Interpreter

• hint, BASIC, …

Page 66: Monad tutorial

モナドの作り方 How to design your monads

Page 67: Monad tutorial

モナドを設計するにあたって

• 自分のラブラリが、モナドとして抽象化できることに気づいたとします

• しかし、モナドの作り方を間違うと、非常に使い勝手の悪いものができてしまいます –使い勝手のよいモナドの実装には気をつけるべきことが沢山あります

• ここまで紹介したことはすべて理解しておくことが望ましいです

Page 68: Monad tutorial

1) 既存のモナドが利用できないか

• 実際のところ、mtlに含まれるモナドで、ほとんどのケースはカバーされます

• 作りたい計算が、mtlにあるモナドの組み合わせで実現できないかまず検討するべきです

Page 69: Monad tutorial

2) モナド変換子版を用意する

• いざモナドを作るとなったら、(原理的に)可能なのであれば、モナド変換子版を必ず用意しましょう

–モナド変換子にはHogeTと、末尾に大文字Tをつけるのが慣習です

• 非モナド変換子版は、モナド変換子版にIdentityモナドを代入したものにします

–実装を重複させてはいけません

Page 70: Monad tutorial

MonadIOクラスのンスタンスにする

• あなたのモナドをMonadIOのンスタンスにしておくのはとても良いことです

• あなたのモナドを利用するすべての場所でIOを行うことができるようになります

• MonadIOを用意しておくのはモナドによってIOを行うHaskellにとっては極めて重要なことです

Page 71: Monad tutorial

※ IOモナドについて(1)

• IOモナドは、Haskellのプログラムの中では外すことのできないモナドです – unsafePerformIO などを除いて

• その結果、IOを呼ぶコードはそれ自身がIOを行わなくても、IOモナドにする必要があります

• Haskellによくある批判として、まともなプログラムを書いているとほとんどすべての関数の型がIOになる、というのがあります – IOモナドは感染するとか言われます

Page 72: Monad tutorial

※ IOモナドについて(2)

• そこでMonadIOの出番です

• IOが必要なコンテクストについてのみ、MonadIOを要求させておけば良くなります

• Pureなモナドに関しては、具体的な型が決定するに従って、自動で持ち上げられることになります

• つまり、PureなコードとIOのコードのオーバーロードが可能になるということです

Page 73: Monad tutorial

3) MonadControlIOの ンスタンスにする

• あなたのモナドが例外を正しく扱えるようにするために、MonadControlIOのンスタンスにしましょう

–これがないと bracket などが正しく後処理できません

Page 74: Monad tutorial

4) MonadTransのンスタンスにする

• あなたのモナド変換子が、他のモナドを自動多段持ち上げ可能になるように(可能であれば)必ず、MonadTransのンスタンスにしましょう

Page 75: Monad tutorial

5) Functor, Applicative のンスタンスにする

• Applicativeスタルというものがあります – http://d.hatena.ne.jp/kazu-yamamoto/20101211/1292021817

– などを参照

• あなたのモナドをFunctor, Applicativeのンスタンスにすると、使い勝手が大幅に向上します

• 必ずこれらのンスタンスにしましょう

– Monadのンスタンスは必ずFunctor及びApplicativeのンスタンスにできます

Page 76: Monad tutorial

6) 必要に応じて、その他

• その他のモナドクラスのンスタンスにします

–エラーハンドリングを付けたいなら、

MonadError

–失敗に対する代替を与えたいときには、

Alternative

Page 77: Monad tutorial

※ Alternativeクラスについて

• m1 <|> m2 なる演算子が定義されている

• m1が失敗したとき、m2の結果

• Alternativeの任意のンスタンスに対して – many p – pを失敗するまで繰り返し

– some p – pを失敗するまで1回以上繰り返し

– optional p – p が失敗したらNothing成功したらJust aを返す

–これらを定義することができる

Page 78: Monad tutorial

GenericNewtypeDeriving

• 自分のモナドをこれらすべてのンスタンスにするのは骨の折れる作業です

• モナドがnewtypeの時、これをderiving で済ませることができます

– -XGenericNewtypeDeriving 言語拡張

Page 79: Monad tutorial

後半まとめ

• モナドを作るにあたって

– MTLのモナドの組み合わせで実現できないか考える

–いろいろなクラスのンスタンスにしておく

–モナド以外の有用なクラスが標準にあるのでそれのンスタンスにもする