「モナド (プログラミング)」の版間の差分
m ロボットによる 変更: ca:Mònada (programació funcional) |
m 外部リンクの修正 http:// -> https:// (wordpress.com) (Botによる編集) |
||
(29人の利用者による、間の194版が非表示) | |||
1行目: | 1行目: | ||
[[関数型プログラミング]]において、'''モナド'''はプログラムを構造化するための汎用的な抽象概念である。対応したプログラム言語では、[[ボイラープレート]]的なコードでもモナドを使って除去することが可能となる。これはモナドが、特定の形をした[[計算]]を表す[[データ型]]と、それに関する生成と合成の2つの[[手続き]]を提供することによって実現されている。生成は''任意の''基本型の値をモナドに包んで'''モナド値'''を生成する手続きであり、合成はモナド値を返す関数('''モナド関数''')たちを合成する手続きである。<ref name="RealWorldHaskell">{{cite book | last1 = O'Sullivan | first1 = Bryan | last2 = Goerzen | first2 = John | last3 = Stewart | first3 = Don | title = Real World Haskell | publisher = O'Reilly Media | location = Sebastopol, California | year = 2009 | isbn = 978-0596514983 | chapter = Monads | at = chapter 14 | chapter-url = http://book.realworldhaskell.org/read/monads.html | url = http://book.realworldhaskell.org/}}</ref> |
|||
[[計算機科学]]における'''モナド'''とは、[[関数型言語]]において[[参照透過性]]を維持しつつ[[手続き型プログラミング|手続き型]]的な記述を行なうための枠組みを言う。主に[[プログラミング言語]][[Haskell]]で用いられる。 |
|||
広い範囲の問題をモナドを使うことで単純化できる。例えば、<code>Maybe</code>モナドを使えば[[未定義値]]への対処が簡単になり、<code>List</code>モナドを使えばリストに入った値を柔軟に扱うことができる。複雑に組み合わさった関数は、モナドを使えば、補助データの管理や制御構造や副作用を除去した簡単なパイプライン構造に置き換えることができる<ref name="RealWorldHaskell" /><ref name="Wadler1990">{{cite conference | author-link = Philip Wadler | last = Wadler | first = Philip | title = Comprehending Monads | conference = ACM Conference on LISP and Functional Programming | location = Nice, France | date = June 1990 | citeseerx = 10.1.1.33.5381}}</ref>。 |
|||
元来は[[入出力|I/O]]等の[[副作用 (プログラム)|副作用]]を伴う処理や、順序処理を自然に記述するために導入された。後に様々な領域でモナド形式による表現の有用性が発見され、現在では「モナド化」はHaskellプログラミング全般に強力な理論基盤を与えている。 |
|||
モナドの概念や用語は[[圏論]]から来ている。圏論においては、モナドは追加の構造を持つ[[関手]]として定義されている{{efn|プログラミングにおいては複数の[[自由変数]]上の関数が使われるため、正確にはここでいうモナドは圏論における[[強モナド]]である。<ref name="Moggi1991" />}}。1980年代の後半に研究され始め、1990年代前半には、一見無関係な計算機科学上の問題がモナドによって統一的で関数的にモデル化できることが分かってきた。圏論はモナドの満たすべき形式的な条件を与え、これは'''モナド則'''と呼ばれている。モナド則はモナド的なコードの[[形式的検証]]にも使用可能である。<ref name="Moggi1991">{{cite journal | author-link = Eugenio Moggi | last = Moggi | first = Eugenio | year = 1991 | title = Notions of computation and monads | journal = Information and Computation | volume = 93 | issue = 1 | pages = 55–92 | url = http://www.disi.unige.it/person/MoggiE/ftp/ic91.pdf | citeseerx = 10.1.1.158.5275 | doi=10.1016/0890-5401(91)90052-4}}</ref><ref name="Wadler1992">{{cite conference | author-link = Philip Wadler | last = Wadler | first = Philip | title = The essence of functional programming | conference = 19th Annual ACM Symposium on Principles of Programming Languages | location = Albuquerque, New Mexico | date = January 1992 | citeseerx = 10.1.1.38.9516}}</ref> |
|||
モナドの名称は、[[圏論]]の[[モナド (圏論)|モナド]](モノイド+トライアド)に基づいており、[[ゴットフリート・ライプニッツ|ライプニッツ]]の[[モナド]](単子論)とは無関係である。 |
|||
ある種の計算では、その計算の[[意味]]をモナドで明確に表現できることを利用して、プログラム言語の便利機能の実装にモナドを使うことができる。[[Haskell]]のように、標準[[ライブラリ]]ですでに汎用のモナド構造といくつかのインスタンスを定義している言語もある<ref name="RealWorldHaskell" /><ref name="GentleIntroHaskell">{{cite book | author-link1 = Paul Hudak | last1 = Hudak | first1 = Paul | last2 = Peterson | first2 = John | last3 = Fasel | first3 = Joseph | title = A Gentle Introduction to Haskell 98 | year = 1999 | chapter = About Monads | at = chapter 9 | chapter-url = https://www.haskell.org/tutorial/monads.html | url = https://www.haskell.org/tutorial/index.html}}</ref>。 |
|||
==概要== |
|||
[[遅延評価]]をデフォルトでもつ純粋関数型言語では、実際の処理がどのタイミングで行なわれるのか予測することが難しく、またシステムの状態を保持して処理の流れを変えるような記述は困難である。 |
|||
==歴史== |
|||
モナドでは処理手順と型を構造化することで、局所的に手続き型に近い記述形式を導入し、自然な形で副作用や順序処理を埋め込んでいる。 |
|||
プログラミングにおけるモナドの概念は1980年代のプログラム言語{{仮リンク|Opal (プログラミング言語)|en|Opal (programming language)}}に見られるが、このときは「コマンド」と呼ばれ形式化はされなかった{{citation needed|date=June 2013}}。 |
|||
1991年に[[Eugenio Moggi]]はプログラムの構造化でモナドを初めて汎用的に使用した<ref name="moggi91">{{cite journal | authorlink = Eugenio Moggi| first=Eugenio | last=Moggi | year = 1991 | title = Notions of computation and monads | journal = Information and Computation | volume = 93 | issue = 1 | pages = | url = http://www.disi.unige.it/person/MoggiE/ftp/ic91.pdf | doi=10.1016/0890-5401(91)90052-4}}</ref>。この成果を元に、[[Philip Wadler]]や[[サイモン・ペイトン・ジョーンズ|Simon Peyton Jones]]といったプログラム言語の研究者(二人ともHaskellの言語仕様に取り組んでいた)により実装が行われた。初期のバージョンのHaskellは問題の多い「遅延リスト」を用いた入出力を使っていたが、Haskell 1.3からは[[遅延評価]]により柔軟に合成可能なモナドによる入出力が導入された。 |
|||
言語が参照透過性を保つ限り、どこかに格納された値を書き換える事は不可能であるが、逆に言えば、どこにも格納されていない値であればどんな数値を取っても良い事になる。関数型言語でこれに適合するのは関数のリターンである。都合の良い事に、関数のリターンは必ず内部で呼び出した関数が全て終了してから行われるため、下位→上位というリターンの順序が崩れる事もない。 |
|||
入出力に加えて、プログラム言語研究者とHaskellのライブラリ設計者は構文解析器やインタープリタにモナドを適用することに成功した。Haskellのdo記法に現れる性質から、モナドの概念は[[applicative functor]]と[[arrow]]へと一般化された。 |
|||
この性質を利用し、順序処理を関数の入れ子に、また関数自体の値と平行して副値を走らせることで「副作用」をパッケージ化するというのがモナドのコンセプトである。通常の文法の範囲内でもモナドと近い処理は可能だが、プログラムが非常に書きづらい形になるため実用的とは言い難い。 |
|||
長い間、Haskellとその派生だけがプログラミングにおけるモナドの主な利用者であった。最近では、[[F Sharp|F#]]が[[コンピュテーション式]]または「ワークフロー」と呼ばれる機能を導入した。これは、命令型プログラムしか経験のないプログラマにもなじみやすい構文を使ったモナド的構成を提供しようという試みである<ref name="seq">{{cite web | url = http://blogs.msdn.com/dsyme/archive/2007/09/22/some-details-on-f-computation-expressions-aka-monadic-or-workflow-syntax.aspx | title = Some Details on F# Computation Expressions | accessdate = 2007-12-14}}</ref>。 |
|||
{{DEFAULTSORT:もなと}} |
|||
[[Category:関数型プログラミング]] |
|||
==動機付けと例== |
|||
{{Computer-stub}} |
|||
プログラム言語Haskellはモナドを多用する関数型の言語であり、簡単にモナドの合成ができるような[[構文糖衣]]を持っている。特に指定がない限り、この記事のコード例はHaskellで書かれている。 |
|||
モナドの紹介として一般的なふたつの例 ''Maybe'' モナドと ''I/O'' モナドを取り上げる。もちろん、モナドはHaskellに限られたものではないので、[[JavaScript]]での ''Writer'' モナドの例もその次に扱う。 |
|||
[[ca:Mònada (programació funcional)]] |
|||
[[de:Monade (Typkonstruktion)]] |
|||
===Maybeモナド=== |
|||
[[en:Monad (functional programming)]] |
|||
[[fi:Monadi (funktionaalinen ohjelmointi)]] |
|||
[[オプション型]] ''Maybe a'' を考えよう。これは型 ''a'' の値をひとつ持つか、全く持たないかのふたつの状態を表現する型である。これらを区別するために、二種類の[[代数的データ型]]構成子を持つ。<code>Just t</code>はひとつの値<code>t</code>を保持し、<code>Nothing</code>は値を何も保持しない。 |
|||
[[fr:Monade (informatique)]] |
|||
<syntaxhighlight lang="haskell"> |
|||
[[it:Monade (informatica)]] |
|||
data Maybe t = Just t | Nothing |
|||
[[ru:Монада (программирование)]] |
|||
</syntaxhighlight> |
|||
[[uk:Монади (програмування)]] |
|||
この型を使って[[検査例外]]の簡単なものを作ってみたいとする。計算の途中で失敗した場合は、残りの計算は飛ばして結果<code>Nothing</code>を返し、すべての計算が成功した場合は何か値<code>x</code>を保持した<code>Just x</code>を返すことにする。 |
|||
[[zh:单子]] |
|||
以下の例では、<code>add</code>は''Maybe Int''型のふたつの引数を受け取って、同じ型の結果を返す。<code>mx</code>と<code>my</code>がともに<code>Just</code>である場合は、これらの値の和を<code>Just</code>に入れて返したい。もし、<code>mx</code>と<code>my</code>のどちらかが<code>None</code>だった場合は、<code>Nothing</code>を返したい。こういった振る舞いをする関数を単純に実装しようとしたら、「if <code>Nothing</code> then <code>Nothing</code> else <code>Just x</code>の<code>x</code>に何かする」の形の場合わけを複数ネストすることになり、すぐやっかいなものになる<ref name="RealWorldHaskell" />。 |
|||
<syntaxhighlight lang="haskell"> |
|||
add :: Maybe Int -> Maybe Int -> Maybe Int |
|||
add mx my = |
|||
case mx of |
|||
Nothing -> Nothing |
|||
Just x -> case my of |
|||
Nothing -> Nothing |
|||
Just y -> Just (x + y) |
|||
</syntaxhighlight> |
|||
これを楽にするための、各計算を繋げる演算子を定義することができる。二項演算子 ''bind'' (<code>>>=</code>) は失敗する可能性のある計算の結果を、もうひとつの失敗する可能性のある計算へ連鎖する。最初の引数が <code>Nothing</code> だった場合は、二番目の引数(関数引数)は無視されて、単に計算全体が失敗する。もし、最初の引数が <code>Just x</code> だった場合は、この値 <code>x</code> は二番目の関数引数に渡されて、この関数の結果として新しい ''Maybe'' の値が返される。これは <code>Just</code> な値か失敗かのどちらかになる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b |
|||
Nothing >>= _ = Nothing -- 失敗した計算は Nothing を返す |
|||
(Just x) >>= f = f x -- 値 x に関数 f を適用する |
|||
</syntaxhighlight> |
|||
計算の状態に影響を与えずに値を投入する構築子は、<code>Just</code> がすでにそうなっている。 |
|||
<syntaxhighlight lang="haskell"> |
|||
return :: a -> Maybe a |
|||
return x = Just x -- 値 x を包んで、型 (Maybe a) の値として返す |
|||
</syntaxhighlight> |
|||
これらを使うことで、上の例は次のように書ける。 |
|||
<syntaxhighlight lang="haskell"> |
|||
add :: Maybe Int -> Maybe Int -> Maybe Int |
|||
add mx my = -- (Maybe Int) 型のふたつの値を足す関数。各入力は Nothing の場合がある |
|||
mx >>= (\x -> -- mx が Nothing でない場合に、値 x を抽出する |
|||
my >>= (\y -> -- my が Nothing でない場合に、値 y を抽出する |
|||
return (x + y))) -- 値(x+y)を包んで、(Maybe Int)型の値として和を返す |
|||
</syntaxhighlight> |
|||
さらにdo記法という構文糖衣を使うことで、次のように書くことができる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
add :: Maybe Int -> Maybe Int -> Maybe Int |
|||
add mx my = do |
|||
x <- mx |
|||
y <- my |
|||
return (x + y) |
|||
</syntaxhighlight> |
|||
この種の操作は頻出なので、Haskellの標準関数(<code>liftM2</code>)が用意されている。これはふたつのモナド的な値(ここでは、ふたつのMaybe)と、その中身(ふたつの数値)を組み合わせる関数(加算)を受け取る関数であり、上の例は次のように書ける。 |
|||
<syntaxhighlight lang="haskell"> |
|||
add :: Maybe Int -> Maybe Int -> Maybe Int |
|||
add = liftM2 (+) |
|||
</syntaxhighlight> |
|||
(上記のdo記法を使ってliftM2の定義を書いてみよう) |
|||
===I/Oモナド=== |
|||
Haskellのような純粋関数型言語では関数が外部から観測可能な作用をもつことは、関数の意味から外れることになるため許されない。しかし、関数が直接副作用を起こすのではなく、求める副作用を''記述する''値を構築して返し、呼び出した側が適当な時に実行することは可能である<ref>[[Simon Peyton Jones|Peyton Jones, Simon L.]]; Wadler, Philip. [http://citeseer.ist.psu.edu/peytonjones93imperative.html Imperative Functional Programming]. Conference record of the Twentieth Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, Charleston, South Carolina. 1993</ref>。Haskellの記法では、型 ''IO a'' の値がこのようなアクションを表現していて、実行されたときには、型 ''a'' の何らかの値を生成する。 |
|||
<code>IO</code>型の値は「現在の世界の状態を引数に取り、関数の返り値として変化した状態を持つ新しい世界を返す」という意味でアクションとみなすことができる。例えば、関数 <code>doesFileExist</code> と <code>removeFile</code> は次の型を持つ関数である。 |
|||
<syntaxhighlight lang="haskell"> |
|||
doesFileExist :: FilePath -> IO Bool |
|||
removeFile :: FilePath -> IO () |
|||
</syntaxhighlight> |
|||
<code>removeFile</code> は関数としては、<code>FilePath</code> を引数にとり、ある <code>IO</code>アクションを返す。このアクションが''実行される''と世界を、この場合はファイルシステムを <code>FilePath</code> という名前のファイルの存在しないものに変更する。この場合は、<code>IO</code> の持っている値は <code>()</code> 型の値なので、なにも結果は生成されず、呼び出し側はこれを気にする必要はない。一方で、<code>doesFileExist</code> の場合は、関数の返す <code>IO</code>アクションはブール型の値、<code>True</code> または <code>False</code>を包んでいて、概念的には、アクションが実行されたときのファイルシステムに <code>FilePath</code> という名前のファイルが存在したかどうかを呼び出し側が知っているという世界の状態を表現している。アクションからアクションへと世界の状態を渡して管理するこの方法により、決まった順序で状態を変化させていくアクションの列を定めることが可能となる。この過程は[[時相論理]]において宣言的な命題のみで時間の経過を表現する手法と似ている。以下の例ではプログラム内のアクションを連鎖する方法の詳細を再び Haskell を使って明らかにする。 |
|||
基本的な種類の入出力操作、[[標準出力]]に文字列を書く、標準入力から文字列を読む、ファイルの読み書き、ネットワーク越しのデータ送信等をすべて記述できるようにしたい。さらに、これらのプリミティブを組み合わせて大きなプログラムを作れる必要もある。例えば次のように書けることが望ましい。 |
|||
<syntaxhighlight lang="haskell"> |
|||
main :: IO () |
|||
main = do |
|||
putStrLn "What is your name?" |
|||
name <- getLine |
|||
putStrLn ("Nice to meet you, " ++ name ++ "!") |
|||
</syntaxhighlight> |
|||
この直観的な記法はどのように形式化できるだろうか?このためには各入出力アクションに対して、いくつかの基本的な操作を実行できる必要がある。 |
|||
* ふたつの入出力アクションをひとつにできなければならない。Haskellでは、中置演算子 <code>>></code> を用いて、<code>putStrLn "abc" >> putStrLn "def"</code> と書き、コンソールに二行の文字列を表示するひとつのアクションである。演算子 <code>>></code> の型は ''IO a → IO b → IO b'' であり、ふたつの入出力アクションを引数に取り、順番に実行するひとつのアクションにまとめて返す。 |
|||
* ''何もしない''という入出力アクションも必要である。これは値を返すがなんの副作用も持たない。Haskellではこのアクションは <code>return</code> で構築され、この型は ''a → IO a'' である。 |
|||
* さらに、最初のアクションの結果に基づいて次に実行するアクションを決める必要がある場合がある。Haskellでは、演算子 <code>>>=</code> (''bind''と読む)を使い、型は ''IO a → (a → IO b) → IO b'' である。左側のオペランドは型 <code>a</code> の値を返す入出力アクションであり、右側のオペランドは左側のアクションの生成した値に応じて次に実行する入出力アクションを選択する関数である。結果は、最初のアクションを実行しその値を関数に渡した評価結果をアクションとして実行し、最後にこのアクションの結果を返す合成されたひとつのアクションとなる。 |
|||
:Haskellのこの演算子を使った例は <code>getLine >>= putStrLn</code> であり、これは標準入力から一行文字列を読み込み、それをそのまま標準出力に書き出す。最初の演算子 <code>>></code> は単にこの演算子の特別な場合である。最初のアクションの結果は無視され、二番目のアクションは常に同じである。 |
|||
これらの3つの演算子をプリミティブな入出力操作として「いかなるプログラムでも」書けるかというのは、データ変換([[ラムダ式]]使う)やif式やループを含めたとしても自明であるとは言いがたい。上の例は長い式を使って次のように書ける。 |
|||
<syntaxhighlight lang="haskell"> |
|||
main = |
|||
putStrLn "What is your name?" >> |
|||
getLine >>= \name -> |
|||
putStrLn ("Nice to meet you, " ++ name ++ "!") |
|||
</syntaxhighlight> |
|||
''bind'' 演算子の持つパイプライン構造により、''getLine'' と ''putStrLn'' の命令はそれぞれ一度だけ、書いた順序で評価され、入力ストリームから文字列を取り出して、出力ストリームに出力する副作用は関数型パイプラインの中でも正しく実行される。これは言語が関数を[[アウト・オブ・オーダー実行]]や[[遅延評価]]する場合でも成立する。 |
|||
明らかに入出力の定義とMaybeの定義は共通の構造を持っているが、異なっている部分も多い。上述した構造や他のよくある似たような多くの構造の抽象化である。モナドの一般化された概念は、計算の一部が副作用を発生させるにもかかわらず、純粋関数的な計算として実行したくなるようなあらゆる状況を包含している。 |
|||
==形式的定義== |
|||
モナドは、元となる[[型システム]]が与えられたとき、その内部に対応する型システム(''モナド的型システム''と呼ぶ)を埋め込む構成である(つまり、各モナド的型は元のシステムの型でもある)。このモナド的型システムは元の型システムの重要な点は全て保存するが、モナドに固有の特徴を追加する{{efn|技術的には、モナドは元となる型を保存する必要はない。例えば、全ての演算子がただひとつの多相な値を返すような自明なモナドもモナドの公理を満たす。逆に、モナドは新しい構造を追加する必要はない。元の型を変更せずそのままとする恒等モナドはモナドの公理を満たし、モナド変換子の再帰の基点として有用である。}}。 |
|||
モナドは複数の方法で定義できる。そのうちの1つは「モナドとは、ある法則(モナド則)を満たす三つ組みである」<ref>A monad is a triple (M, unit,>>=) consisting of a type constructor M and two operations of the following types |
|||
Tomas Petricek (2018) What we talk about when we talk about monads. The Art, Science, and Engineering of Programming, 2018, Vol. 2, Issue 3, Article 12. [https://doi.org/10.22152/programming-journal.org/2018/2/12]</ref> で表される定義である。すなわち圏論の[[クライスリ圏]]における[[クライスリトリプル]]である。 |
|||
=== 三つ組 === |
|||
三つ組は以下の要素からなる。 |
|||
{| class="wikitable" |
|||
|+表. モナドを構成する三つ組 |
|||
!名前 |
|||
!別名 |
|||
!定義式 |
|||
!説明 |
|||
|- |
|||
|型構築子M |
|||
| |
|||
| |
|||
|型 <code>x</code> から派生した別の型を得る(型から型を作る)演算子 |
|||
|- |
|||
|unit関数 |
|||
|return |
|||
|<syntaxhighlight lang="haskell">unit :: x -> M x</syntaxhighlight> |
|||
|型 <code>x</code> の値を型 <code>M x</code> の値へうつす多相な関数 |
|||
|- |
|||
|bind関数 |
|||
|<code>>>=</code> |
|||
|<syntaxhighlight lang="haskell">bind :: M x -> (x -> M y) -> M y</syntaxhighlight> |
|||
|型 <code>M x</code> の値、型 <code>x</code> の値を型 <code>M y</code> の値へ写す関数の2つから <code>M y</code> を得る関数 |
|||
|} |
|||
==== ''型構築子M'' ==== |
|||
<syntaxhighlight lang="haskell"> |
|||
class Monad M where |
|||
... |
|||
value :: M x |
|||
</syntaxhighlight>型構築子''M''は、ある型 <code>x</code> から派生した型を得る方法を定める<ref>The monad type constructor <code>m</code> is added to function results (modulo currying) and nowhere else. |
|||
Hackage. Control.Monad [http://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Monad.html#g:1]</ref>。得られる型はしばしば <code>M x</code> で表記される。 |
|||
==== ''unit 関数'' ==== |
|||
''unit''関数( <code>return</code> とも{{efn|この名前は後述する do 記法での使われ方から来ている。}})は型 <code>x</code> の引数を型 <code>M x</code> の返り値にうつす関数である。すなわち''unit'' 関数は型 <code>x</code> の値をモナド型 <code>M x</code> の値へうつす多相な関数である。引数値変換の有無は定義されない(一般には値を変換せずに保持する(例:入力 <code>1</code> =>出力 <code>Maybe(1)</code>)。 |
|||
==== ''bind 関数'' ==== |
|||
bind関数( <code>>>=</code> とも<ref>The >>= operator is known as bind |
|||
Tomas Petricek (2018) What we talk about when we talk about monads. The Art, Science, and Engineering of Programming, 2018, Vol. 2, Issue 3, Article 12. [https://doi.org/10.22152/programming-journal.org/2018/2/12]</ref>)は「モナド的値」と「値をモナド的値へうつす関数」を引数にとり、第1引数のモナド的値を第2引数関数の値域へうつす。 |
|||
===モナド則=== |
|||
三つ組 {''M'', ''unit(return)'', ''bind(>>=)''} をモナドと成す規則をモナド則という<ref>{{cite web|url=http://www.haskell.org/haskellwiki/Monad_laws|title=Monad laws|work=HaskellWiki|publisher=haskell.org|accessdate=2011-12-11}}</ref>。以下がモナド則である。 |
|||
* ''return'' は <tt>>>=</tt> に対して、ほとんど[[単位元]]である。 |
|||
** <syntaxhighlight lang="haskell">(return x) >>= f ≡ f x</syntaxhighlight> |
|||
** <syntaxhighlight lang="haskell" >m >>= return ≡ m</syntaxhighlight> |
|||
* ふたつの関数を続けて bind するのは、これらの関数から決まるひとつの関数を bind することに等しい。 |
|||
** <syntaxhighlight lang="haskell">(m >>= f) >>= g ≡ m >>= ( \x -> (f x >>= g) )</syntaxhighlight> |
|||
公理を do 記法で表現することもできる。 |
|||
* <syntaxhighlight lang="haskell">do { f x } ≡ do { v <- return x; f v }</syntaxhighlight> |
|||
* <syntaxhighlight lang="haskell">do { m } ≡ do { v <- m; return v }</syntaxhighlight> |
|||
* <syntaxhighlight lang="haskell">do { x <- m; y <- f x; g y } ≡ do { y <- do { x <- m; f x }; g y }</syntaxhighlight> |
|||
また、モナド合成演算子 <syntaxhighlight lang="haskell">(f >=> g) x = (f x) >>= g</syntaxhighlight> を使うと |
|||
*<syntaxhighlight lang="haskell">return >=> g ≡ g</syntaxhighlight> |
|||
*<syntaxhighlight lang="haskell">f >=> return ≡ f</syntaxhighlight> |
|||
*<syntaxhighlight lang="haskell">(f >=> g) >=> h ≡ f >=> (g >=> h)</syntaxhighlight> |
|||
===fmap と join=== |
|||
Haskellではモナドを ''return'' と ''bind'' 関数を用いて定義するが、''return'' にふたつの演算子 ''join'' と ''fmap'' を加えることでも定義可能である。この定式化は圏論における元のモナドの定義により近くなる。''fmap'' 演算子は型 (''t''→''u'') → M ''t''→M ''u''<ref>{{cite web|title=Functors, Applicative Functors and Monoids|publisher=learnyouahaskell.com|url=http://learnyouahaskell.com/functors-applicative-functors-and-monoids|accessdate=2015-02-21}}</ref> を持ち、ふたつの型間の関数を受け取って、モナドの中の値に「同じこと」をする関数を返す。''join'' 演算子は型 M (M ''t'')→M ''t'' を持ち、二層になったモナド的な情報を平らにしてひとつにする。 |
|||
ふたつの定式化は以下の関係を持つ。 |
|||
<syntaxhighlight lang="haskell"> |
|||
fmap f m = m >>= (return . f) |
|||
join n = n >>= id |
|||
m >>= g ≡ join (fmap g m) |
|||
</syntaxhighlight> |
|||
ここで、m は M ''t''、n は M (M ''r'')、f は ''t'' → ''u''、g は t → M ''v'' の型を持つ。''t''、''r''、''u''、''v'' は元の型である。 |
|||
モナドでなくても、型と関数からなる圏では fmap 関数は任意の[[函手]]に対して定義できる。函手は次の法則を満たす。 |
|||
<syntaxhighlight lang="haskell"> |
|||
fmap id ≡ id |
|||
fmap (f . g) ≡ (fmap f) . (fmap g) |
|||
</syntaxhighlight> |
|||
同じ圏で、''return'' は{{仮リンク|基点付き函手|en|pointed functor}}を特徴付ける。これは値を函手の中に「持ち上げる」ことによる。満たすべき法則は次の通りである。 |
|||
<syntaxhighlight lang="haskell"> |
|||
return . f ≡ fmap f . return |
|||
</syntaxhighlight> |
|||
さらに、''join'' によってモナドが特徴付けられる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
join . fmap join ≡ join . join |
|||
join . fmap return ≡ join . return = id |
|||
join . fmap (fmap f) ≡ fmap f . join |
|||
</syntaxhighlight> |
|||
===加法的モナド=== |
|||
'''加法的モナド'''は、[[モノイド]]則を満たす二項演算子 ''mplus''とその単位元としてモナド的な値 ''mzero'' を備えたモナドである。演算子 ''mplus'' の型は ''M t'' → ''M t'' → ''M t'' であり(ここで、''M'' はモナドの構築子であり ''t'' は元の型である)、[[結合律]]と左右からの単位元律を満たす。 |
|||
<syntaxhighlight lang="haskell"> |
|||
(a `mplus` b) `mplus` c = a `mplus` (b `mplus` c) |
|||
m `mplus` mzero ≡ mzero `mplus` m ≡ m |
|||
</syntaxhighlight> |
|||
このことから、加法的モナドはモノイドでもある。<code>>>=</code>に対しては、mzeroはヌル要素として振舞う。ゼロを掛けるとゼロになるように、''mzero'' と関数を bind すると結果はゼロとなる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
mzero >>= f ≡ mzero |
|||
</syntaxhighlight> |
|||
同様に、モナド的な値 ''m'' とつねにゼロを返す関数を bind すると、結果はゼロである。 |
|||
<syntaxhighlight lang="haskell"> |
|||
m >>= (\x -> mzero) ≡ mzero |
|||
</syntaxhighlight> |
|||
直観的には、モナド的値としてのゼロはモナドに関連した構造だけを持ち元の型の値を持たない。Maybe モナドでは、「Nothing」がゼロである。リストモナドでは、空リストがゼロである。 |
|||
==構文糖衣: do記法== |
|||
''bind'' 演算子 <code>>>=</code> を直接使ってプログラムを書くのがよい場合もあるが、典型的には''do記法'' (OCamlでは ''perform記法''、F#では''コンピュテーション式'')を使用し、命令型言語のような見た目にできる。コンパイラはdo記法の式を <code>>>=</code> を使ったものに変換する。例えば |
|||
<syntaxhighlight lang="haskell"> |
|||
a = do x <- [3..4] |
|||
[1..2] |
|||
return (x, 42) |
|||
</syntaxhighlight> |
|||
を変換した結果は以下となる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
a = [3..4] >>= (\x -> [1..2] >>= (\_ -> return (x, 42))) |
|||
</syntaxhighlight> |
|||
リストモナドの実装を見てみよう。リストにマップを行い結合を行う(平らにする)この関数は concatMap とも呼ばれる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
instance Monad [] where |
|||
m >>= f = concat (map f m) |
|||
return x = [x] |
|||
fail s = [] |
|||
</syntaxhighlight> |
|||
よって、以下のような変形が行われて、それぞれの式はすべて等価である。 |
|||
<syntaxhighlight lang="haskell"> |
|||
a = [3..4] >>= (\x -> [1..2] >>= (\_ -> return (x, 42))) |
|||
a = [3..4] >>= (\x -> concatMap (\_ -> return (x, 42)) [1..2] ) |
|||
a = [3..4] >>= (\x -> [(x,42),(x,42)] ) |
|||
a = concatMap (\x -> [(x,42),(x,42)] ) [3..4] |
|||
a = [(3,42),(3,42),(4,42),(4,42)] |
|||
</syntaxhighlight> |
|||
リスト<tt>[1..2]</tt>は使われないことに注意しよう。左矢印をつけないことで、連鎖した関数は引数を無視するように変換されて、値ではなくモナド的な構造だけが問題になることを示すことができる。例えば、状態モナドで値を生成せずに状態だけを変化させるようなときに使われる。do記法は <code>>>=</code> の単なる構文糖衣であるので、どんなモナドに対しても使うことができる。 |
|||
Maybeモナドの値を安全に割り算する以下の定義も等価である。 |
|||
<syntaxhighlight lang="haskell"> |
|||
x // y = do |
|||
a <- x -- xとyの中に値がある場合は取り出す |
|||
b <- y |
|||
if b == 0 then Nothing else Just (a / b) |
|||
x // y = x >>= (\a -> y >>= (\b -> if b == 0 then Nothing else Just (a / b))) |
|||
</syntaxhighlight> |
|||
同様にF#ではコンピュテーション式を使うことができる。 |
|||
<syntaxhighlight lang="ocaml"> |
|||
let readNum () = |
|||
let s = Console.ReadLine() |
|||
let succ,v = Int32.TryParse(s) |
|||
if (succ) then Some(v) else None |
|||
let secure_div = |
|||
maybe { |
|||
let! x = readNum() |
|||
let! y = readNum() |
|||
if (y = 0) |
|||
then None |
|||
else return (x / y) |
|||
} |
|||
</syntaxhighlight> |
|||
このmaybeブロックは構文糖衣であり、以下の式に変換される。 |
|||
<syntaxhighlight lang="ocaml"> |
|||
maybe.Delay(fun () -> |
|||
maybe.Bind(readNum(), fun x -> |
|||
maybe.Bind(readNum(), fun y -> |
|||
if (y=0) then None else maybe.Return(x / y)))) |
|||
</syntaxhighlight> |
|||
== モナド的な汎用関数 == |
|||
安全な割り算の結果は、値が <tt>Nothing</tt> であるかを素直に確認せずに(つまり割り算の結果があるかを確認せずに)そのまま計算に使えたほうが便利なことがある。これは「{{仮リンク|持ち上げ|en|Lift (mathematics)}}」関数を使うことで可能である。この関数は <tt>Maybe</tt> に限定されず、任意のモナドに対して定義される。Haskellでは <tt>LiftM2</tt> と呼ばれる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c |
|||
liftM2 op mx my = do |
|||
x <- mx |
|||
y <- my |
|||
return (op x y) |
|||
</syntaxhighlight> |
|||
関数型を示す矢印が右結合であることから、<tt>liftM2</tt>は2引数の関数を引数に取って、さらなる2引数の関数を返す。型シグネチャは <tt>m</tt> がモナドのとき、任意の2引数関数を「持ち上げ」ることができることを示している。例えば、 |
|||
<syntaxhighlight lang="haskell"> |
|||
(.*.) :: (Monad m, Num a) => m a -> m a -> m a |
|||
x .*. y = liftM2 (*) x y |
|||
</syntaxhighlight> |
|||
は演算子 <tt>(.*.)</tt> を定義し、それは引数がどちらも <tt>Nothing</tt> でないときに、掛け算した結果を返す(どちらかが <tt>Nothing</tt> の場合は <tt>Nothing</tt> を返す)。この方法の利点はモナドの実装に深入りする必要がないことである。他の同じような関数やモナドを使う場合でも、<tt>liftM2</tt> を使うことで、やりたいことが鮮明になる([[コードの再利用]]を見よ)。 |
|||
数学的には、liftM2 は次のように定義される。 |
|||
:<math>\text{liftM2} \colon \forall M \colon \text{monad}, \; (A_1 \to A_2 \to R) \to M \, A_1 \to M \, A_2 \to M \, R = </math><math> op \mapsto m_1 \mapsto m_2 \mapsto \text{bind} \; m_1 \; (a_1 \mapsto \text{bind} \; m_2 \; (a_2 \mapsto \text{return} \; (op \, a_1 \, a_2)))</math> |
|||
==他の例== |
|||
===恒等モナド=== |
|||
もっとも単純なモナドは恒等モナドであり、値になんの情報も付加しない。 |
|||
<syntaxhighlight lang="haskell"> |
|||
Id t = t |
|||
return x = x |
|||
x >>= f = f x |
|||
</syntaxhighlight> |
|||
このモナドのdoブロックは変数の代入を実行する。例えば、<tt>do {x <- 2; return (3*x)}</tt> の結果は6である。 |
|||
圏論的な見方をすると、恒等モナドは[[恒等函手]]の間の[[随伴関手|随伴]]から得られる。 |
|||
===コレクション=== |
|||
[[リスト (抽象データ型)|リスト]]や[[セット (抽象データ型)|集合]]や[[多重集合]]といった、おなじみの[[コンテナ (データ型)|コレクション]]型にもモナドであるものがある。リストの場合の定義は次のようになる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
-- "return" constructs a one-item list. |
|||
return x = [x] |
|||
-- "bind" concatenates the lists obtained by applying f to each item in list xs. |
|||
xs >>= f = concat (map f xs) |
|||
-- The zero object is an empty list. |
|||
mzero = [] |
|||
</syntaxhighlight> |
|||
{{仮リンク|リスト内包記法|en|List comprehension}}はリストモナドに固有のもので、例えば、リスト内包 <tt>[ 2*x | x <- [1..n], isOkay x]</tt> はリストモナドでの計算 <tt>do {x <- [1..n]; if isOkay x then return (2*x) else mzero;}</tt> に対応している。 |
|||
リスト内包記法は集合の[[集合#記法|内包記法]]に似ているが、集合はモナドにならない。なぜなら、計算の型は等しいかどうかを比較可能でなければならない — モナドは計算の型についてこれを課していないように見えるにもかかわらず — という制限があるからである。実際、集合は'''制限されたモナド(restricted monad)'''である<ref>[http://www.randomhacks.net/articles/2007/03/15/data-set-monad-haskell-macros How to make Data.Set a monad] shows an implementation of the Set restricted monad in Haskell</ref>。コレクションによるモナドは非決定計算を自然に表現する。リスト(または他のコレクション)は非決定的な計算の異なった実行パスが今の時点で取りうる結果の全体を表す。例えば、<tt>x <- [1,2,3,4,5]</tt> を実行したとすると、変数 <tt>x</tt> はこのリストから非決定的に選ばれた値であるとみなしている。<tt>x</tt>を返すことは、各実行パスの結果を値のリストにまとめることを意味する。さらに、bind 演算子では今の可能な結果に対して関数 <tt>f</tt> を実行し、それぞれの結果をひとつのリストに結合する。 |
|||
<tt>if condition x y then return () else mzero</tt>の形の文はよく現れる。条件が真である場合は何もしない計算を非決定的に選択し、意味のある値は返さない。一方で、条件が偽の場合は、選択肢の存在しない非決定な値 <tt>mzero = []</tt> を返す。つまり、この実行パスを終了し、他の実行パスは走り続ける。これは条件を満たす特定の実行パスだけを通す「ガード」の役割を果たす。よって、コレクションモナドは論理パズルや数独などの問題を解く場合に役に立つ。 |
|||
Haskellのように[[遅延評価]]を行う言語では、リストはその要素が必要になって初めて評価される。例えばリストの最初の要素を要求した場合は、最初の要素だけが計算される。非決定計算にリストモナドを使用した場合には、全ての計算からなる結果は非決定的に遅延リストとして生成され、最初の要素を要求すると、必要最小限の計算のみが行われて、最初の結果を返す。この過程はバックトラックに対応している。ある実行パスにいて計算に失敗した(<tt>mzero</tt>に評価された)場合、最後に行った分岐まで[[バックトラック]]を行い、そこから次のパスを選んで実行を続ける。二番目の要素が要求された場合、再びその答を得るのに必要な最小限の計算が行われる。よって、遅延評価の言語ではリストモナドを使って簡単にバックトラックを実装することができる。 |
|||
圏論的な見方をすると、コレクションモナドは集合の圏とモノイドの圏の間の{{仮リンク|自由函手|en|free functor}}と[[忘却函手]]の随伴から作られる。 |
|||
{| class="wikitable" |
|||
|- |
|||
! コレクションの種類 |
|||
! モノイドの種類 |
|||
|- |
|||
| リスト |
|||
| モノイド |
|||
|- |
|||
| 有限多重集合 |
|||
| 可換モノイド |
|||
|- |
|||
| 有限集合 |
|||
| 冪等可換モノイド |
|||
|- |
|||
| finite permutation |
|||
| 冪等非可換モノイド |
|||
|} |
|||
===状態モナド=== |
|||
状態モナドは状態を付加した計算の型である。好きな型に対して、その型を状態として持つ状態モナドを作ることができる。これは状態を引数に取り、通常の返り値(型 <tt>t</tt> の値)と新しい状態(型 <tt>s</tt> の値)を返す関数である。 |
|||
<syntaxhighlight lang="haskell"> |
|||
type State s t = s -> (t, s) |
|||
</syntaxhighlight> |
|||
今までに見たモナドと違い、状態の型という型引数を取ることに注意しよう。モナドの演算は次のように定義される。 |
|||
<syntaxhighlight lang="haskell"> |
|||
-- "return" は状態を更新せず、受け取った値を生成する |
|||
return x = \s -> (x, s) |
|||
-- "bind" は m が変更した状態を f に渡して結果とする |
|||
m >>= f = \r -> let (x, s) = m r in (f x) s |
|||
</syntaxhighlight> |
|||
状態を操作する関数を挙げる。 |
|||
<syntaxhighlight lang="haskell"> |
|||
get = \s -> (s, s) -- 現時点での状態を見る |
|||
put s = \_ -> ((), s) -- 状態を上書きする |
|||
modify f = \s -> ((), f s) -- 状態を更新する |
|||
</syntaxhighlight> |
|||
初期状態を指定して状態モナドを実行する関数。 |
|||
<syntaxhighlight lang="haskell"> |
|||
runState :: State s a -> s -> (a, s) |
|||
runState t s = t s |
|||
</syntaxhighlight> |
|||
状態モナドのdoブロック内の計算は状態の確認や更新を行うことができる。 |
|||
非形式的に書くと、状態の型 ''S'' を持つ状態モナドは返り値の型 ''T'' を関数型 <math>S \rarr T \times S</math> に変換する。''return'' は単に |
|||
:<math>\text{return} \colon T \rarr S \rarr T \times S = t \mapsto s \mapsto (t, s)</math> |
|||
であり、''bind'' は |
|||
:<math>\text{bind} \colon (S \rarr T \times S) \rarr (T \rarr S \rarr T' \times S) \rarr S \rarr T' \times S</math><math> \ = m \mapsto k \mapsto s \mapsto (k \ t \ s') \quad \text{where} \; (t, s') = m \, s</math> |
|||
である。 |
|||
圏論的な見方では、状態モナドは[[デカルト閉圏]]の定義に現れる積函手と指数函手の随伴から作られる。 |
|||
===環境モナド=== |
|||
環境モナド(''Readerモナド''や''関数モナド''とも呼ばれる)は環境を共有した計算を可能にする。型構築子は型 ''T'' を 関数型 ''E'' → ''T'' にうつす。ここで、''E'' は共有したい環境の型である。モナド演算子は |
|||
:<math>\text{return} \colon T \rarr E \rarr T = t \mapsto e \mapsto t</math> |
|||
:<math>\text{bind} \colon (E \rarr T) \rarr (T \rarr E \rarr T') \rarr E \rarr T' = r \mapsto f \mapsto e \mapsto f \, (r \, e) \, e</math> |
|||
で定義される。 |
|||
環境を操作する関数として |
|||
:<math>\text{ask} \colon E \rarr E = \text{id}_E</math> |
|||
:<math>\text{local} \colon (E \rarr E) \rarr (E \rarr T) \rarr E \rarr T = f \mapsto c \mapsto e \mapsto c \, (f \, e)</math> |
|||
がある。''ask'' は現在の環境を取得するのに使う。''local''は指定した計算を一時的に変更した環境で行う。状態モナドと同様に、環境モナドによる計算は単に環境の値にモナドの値を適用することで実行できる。 |
|||
===Writerモナド=== |
|||
Writerモナドは本来の計算を行いながら、様々な種類の追加の出力を作成することができる計算である。ログ取りやプロファイリングに有用である。元の型 ''T'' に対して、Writerモナドは型 ''W'' × ''T'' の値を持つ。ここで、''W'' はモノイド則を満たす演算を持った型である。すなわち、''W'' は二項演算子 ''(a,b) → a*b'' と単位元 ''ε'' を持っており、これは結合則 ''(a*b)*c = a*(b*c)'' を満たし、任意の ''x'' について、''x*ε = ε*x = x'' を満たす。 |
|||
モナド演算は |
|||
:<math>\text{return} \colon T \rarr W \times T = t \mapsto (\epsilon, t)</math> |
|||
:<math>\text{bind} \colon (W \times T) \rarr (T \rarr W \times T') \rarr W \times T' = (w, t) \mapsto f \mapsto (w * w',\, t') \quad \text{where} \; (w', t') = f \, t</math> |
|||
で定義される。ここで、εと * はモノイド W の単位元と二項演算子である。 |
|||
JavaScriptではWriterモナドの値として2要素の配列を使うことができる。最初の要素は任意の値であり、二番目の要素は出力を貯めておくための配列である。 |
|||
<syntaxhighlight lang="Javascript"> |
|||
var writer = [ value, [] ]; |
|||
</syntaxhighlight> |
|||
角括弧はこのモナドの値コンストラクタとして使っていて、より単純な要素(配列の0番目は値、1番目にはログ配列)からWriterモナドの値を作っている。 |
|||
''unit'' (''return'' はJavaScriptの予約語なのでこちらを使う)は元の値に空のログ配列をつけるだけである。 |
|||
<syntaxhighlight lang="Javascript"> |
|||
var unit = function(value) { return [ value, [] ]; }; |
|||
</syntaxhighlight> |
|||
''bind'' は最初のWriterモナドの中の値に関数を適用し、それぞれのログ配列を結合して新しいモナドの値として返す。 |
|||
<syntaxhighlight lang="Javascript"> |
|||
var bind = function(writer, transform) { |
|||
var value = writer[0]; |
|||
var log = writer[1]; |
|||
var result = transform(value); |
|||
return [ result[0], log.concat(result[1]) ]; |
|||
}; |
|||
</syntaxhighlight> |
|||
''pipeline''は補助関数で関数の配列を順番に ''bind'' する。 |
|||
<syntaxhighlight lang="Javascript"> |
|||
var pipeline = function(writer, transforms) { |
|||
var result = writer; |
|||
transforms.forEach(function (transform) { |
|||
result = bind(result, transform); |
|||
}); |
|||
return result; |
|||
}; |
|||
</syntaxhighlight> |
|||
モナドの値を返す関数の例を挙げる。 |
|||
<syntaxhighlight lang="Javascript"> |
|||
var squared = function(x) { |
|||
return [ x * x, 'was squared.' ]; |
|||
}; |
|||
var halved = function(x) { |
|||
return [ x / 2, 'was halved.' ]; |
|||
}; |
|||
</syntaxhighlight> |
|||
最後に数学関数のパイプラインにデバッグ情報(デバッグ情報の配列は結合されて、値と一緒に返される)を追加する例を挙げる。 |
|||
<syntaxhighlight lang="Javascript"> |
|||
pipeline(unit(4), [ squared, halved ]); // [ 8, [ 'was squared.', 'was halved.' ] ] |
|||
</syntaxhighlight> |
|||
===継続モナド=== |
|||
結果の型 <math>R</math> の[[継続]]モナドは型 <math>T</math> を関数型 <math>\left( T \rarr R \right) \rarr R</math> にうつす。これは[[継続渡しスタイル]]のモデルとして使われる。return と bind 関数の定義は |
|||
:<math>\text{return} \colon T \rarr \left( T \rarr R \right) \rarr R = t \mapsto f \mapsto f \, t</math> |
|||
:<math>\text{bind} \colon \left( \left( T \rarr R \right) \rarr R \right) \rarr \left( T \rarr \left( T' \rarr R \right) \rarr R \right) \rarr \left( T' \rarr R \right) \rarr R</math><math>= c \mapsto f \mapsto k \mapsto c \, \left( t \mapsto f \, t \, k \right)</math> |
|||
である。 |
|||
{{仮リンク|call-with-current-continuation|en|call-with-current-continuation}}関数は次で定義できる。 |
|||
:<math>\text{call/cc} \colon \left( \left( T \rarr \left( T' \rarr R \right) \rarr R \right) \rarr \left( T \rarr R \right) \rarr R \right) \rarr \left( T \rarr R \right) \rarr R</math><math> = f \mapsto k \mapsto \left( f \left( t \mapsto x \mapsto k \, t \right) \, k \right)</math> |
|||
===他の例=== |
|||
研究者により発見されたモナドになる概念を挙げる。 |
|||
*[[Iteratee]] |
|||
*[[例外処理]] |
|||
*[[グラフィカルユーザインタフェース]] |
|||
*[[プロセス間通信]] |
|||
*[[構文解析器]] |
|||
*[[インタープリタ]] |
|||
*[[正格評価]] |
|||
*他の言語で書かれたコードへのインターフェース |
|||
== 自由モナド == |
|||
自由モナドは[[自由モノイド]]と似ていて、直観的には、使う型に依存せずにモナド則(またはモノイド則)を満たすようにできる汎用の構造である。 |
|||
型 t に対して、<code>t</code> の自由モノイドは <code>[t]</code> であり、結合的な二項演算子として <code>++</code>、単位元として <code>[]</code> を選ぶ。Haskellでは以下のように書ける。 |
|||
<syntaxhighlight lang="haskell"> |
|||
instance Functor [] where |
|||
fmap _ [] = [] |
|||
fmap fun (x:xs) = fun x : fmap fun xs |
|||
instance Monoid [t] where |
|||
mappend xs ys = xs ++ ys |
|||
mempty = [] |
|||
</syntaxhighlight> |
|||
具体的なモノイドとでは、値 <code>t1,t2,...,tn</code> を二項演算で足すことができるが、<code>[]</code> では単に、結合 <code>[t1,t2,...,tn]</code> になるだけであり、単に「一緒に集めた」だけである。ここで、「一緒に集めた」結果がどうなるべきかは定めない。 |
|||
自由モナドも同じ考えに基づいて定義される。上の定義 <code>List t = Nil | Cons t (List t)</code> に函手を挿入することで、次の定義を得る。 |
|||
<syntaxhighlight lang="haskell"> |
|||
data Free f a = Return a | Bind (f (Free f a)) |
|||
instance Functor f => Functor (Free f) where |
|||
fmap fun (Return x) = Return (fun x) |
|||
fmap fun (Bind x) = Bind (fmap (fmap fun) x) |
|||
instance Functor f => Monad (Free f) where |
|||
return x = Return x |
|||
x >>= fun = Bind (fmap (>>= fun) x) |
|||
</syntaxhighlight> |
|||
<code>List</code> が値のリストを保存するのと違って、<code>Free</code> はドメインの値を決めた函手のリストを保存する。<code>Free</code> の <code>Functor</code> と <code>Monad</code> インスタンスの定義によると、受け取った関数を <code>fmap</code> でリストに落としているだけしかしていないことが分かる。 |
|||
== コモナド == |
|||
コモナドはモナドの[[圏論的な双対]]である。型構築子 W ''T'' と2つの演算 ''extract'' と ''extend'' を持つ。''extract'' の型は W ''T'' → ''T'' であり、''extend'' の型は (W ''T'' → ''T' '') → W ''T'' → W ''T' '' であり、以下のコモナド則を満たすとする。 |
|||
:<math>\text{extend} \,\, \text{extract} = \text{id}</math> |
|||
:<math>\text{extract} \circ (\text{extend} \, f) = f</math> |
|||
:<math>(\text{extend} \, f) \circ (\text{extend} \, g) = \text{extend} \, (f \circ (\text{extend} \, g))</math> |
|||
他の方法として、''fmap'' と ''extract'' と ''duplicate'' を使った定義もできる。''fmap'' と ''extract'' により W は余基点付き函手となる。''duplicate'' がコモナドを特徴付ける。''duplicate'' の型は W ''T'' → W (W ''T'') であり、次の規則を満たす。 |
|||
:<math>\text{extract} \circ \text{duplicate} = \text{id}</math> |
|||
:<math>\text{fmap} \, \text{extract} \circ \text{duplicate} = \text{id}</math> |
|||
:<math>\text{duplicate} \circ \text{duplicate} = \text{fmap} \, \text{duplicate} \circ \text{duplicate}</math> |
|||
ふたつの定式化の関係は |
|||
:<math>\text{fmap}: (A \rarr B) \rarr \mathrm{W} \, A \rarr \mathrm{W} \, B = f \mapsto \text{extend} \, (f \circ \text{extract})</math> |
|||
:<math>\text{duplicate}: \mathrm{W} \, A \rarr \mathrm{W} \, \mathrm{W} \, A = \text{extend} \, \text{id}</math> |
|||
:<math>\text{extend}: (\mathrm{W} \, A \rarr B) \rarr \mathrm{W} \, A \rarr \mathrm{W} \, B = f \mapsto (\text{fmap} \, f) \circ \text{duplicate}</math> |
|||
で与えられる。 |
|||
モナドが副作用を表現すると言われているのに対して、コモナド ''W'' は一種の「文脈」を表している。''extract'' は文脈から値を取り出す関数である。''extend'' は型 W ''A'' → ''B'' を持つ「文脈依存の関数」を合成してパイプラインを作る。 |
|||
===恒等コモナド=== |
|||
恒等コモナドはもっとも単純なコモナドであり、型 ''T'' をそれ自身にうつす。''extract''は恒等関数であり、''extend''は関数適用である。 |
|||
===積コモナド=== |
|||
積コモナドは型 <math>T</math> を積の型 <math>C \times T</math> にうつす。ここで <math>C</math> はコモナドの文脈の型である。コモナド演算は |
|||
:<math>\text{extract}: (C \times T) \rarr T = (c, t) \mapsto t</math> |
|||
:<math>\text{extend}: ((C \times A) \rarr B) \rarr C \times A \rarr C \times B = f \mapsto (c, a) \mapsto (c, f \, (c, a))</math> |
|||
:<math>\text{fmap}: (A \rarr B) \rarr (C \times A) \rarr (C \times B) = (c, a) \mapsto (c, f \, a)</math> |
|||
:<math>\text{duplicate}: (C \times A) \rarr (C \times (C \times A)) = (c, a) \mapsto (c, (c, a))</math> |
|||
で定める。 |
|||
===関数コモナド=== |
|||
関数コモナドは型 <math>T</math> を関数型 <math>M \rarr T</math> にうつす。ここで、<math>M</math> はモノイド構造を持つ型である。コモナド演算は |
|||
:<math>\text{extract}: (M \rarr T) \rarr T = f \mapsto f \, \varepsilon</math> |
|||
:<math>\text{extend}: ((M \rarr A) \rarr B) \rarr (M \rarr A) \rarr M \rarr B = f \mapsto g \mapsto m \mapsto f \, (m' \mapsto g \, (m * m'))</math> |
|||
:<math>\text{fmap}: (A \rarr B) \rarr (M \rarr A) \rarr M \rarr B = f \mapsto g \mapsto (f \circ g)</math> |
|||
:<math>\text{duplicate}: (M \rarr A) \rarr M \rarr (M \rarr A) = f \mapsto m \mapsto m' \mapsto f \, (m * m')</math> |
|||
で定める。εと * はモノイド <math>M</math> の単位元と二項演算子である。 |
|||
===余状態コモナド=== |
|||
余状態コモナドは型 <math>T</math> を型 <math>(S \rarr T) \times S</math> にうつす。ここで S はストアの型である。コモナド演算は |
|||
:<math>\text{extract}: ((S \rarr T) \times S) \rarr T = (f, s) \mapsto f \, s</math> |
|||
:<math>\text{extend}: (((S \rarr A) \times S) \rarr B) \rarr ((S \rarr A) \times S) \rarr (S \rarr B) \times S \,</math><math>= f \mapsto (g, s) \mapsto ((s' \mapsto f \, (g, s')), s)</math> |
|||
:<math>\text{fmap}: (A \rarr B) \rarr ((S \rarr A) \times S) \rarr (S \rarr B) \times S = f \mapsto (f', s) \mapsto (f \circ f', s)</math> |
|||
:<math>\text{duplicate}: ((S \rarr A) \times S) \rarr (S \rarr ((S \rarr A) \times S)) \times S = (f, s) \mapsto ((s' \mapsto (f, s')), s)</math> |
|||
で定義される。 |
|||
==脚注== |
|||
=== 注釈 === |
|||
{{notelist}} |
|||
=== 出典 === |
|||
{{Reflist}} |
|||
== 関連項目 == |
|||
* {{仮リンク|アロー (関数型プログラミング)|en|Arrows in functional programming}} - モナドが計算の結果に作用を追加するのに対して、アローは入力を同様に一般化する。 |
|||
* [[アスペクト指向プログラミング]] - 二次的であったりサポート用の機能を分離してモジュール性を上げるパラダイムである。 |
|||
* [[Effect system]] - 型で副作用を記述する異なった手法。 |
|||
* [[制御の反転]] - 再利用可能なソフトウェアから特定の関数を呼ぶ際の抽象的な原則である。 |
|||
* {{仮リンク|モナド変換子|en|Monad transformer}} - モナドを合成する便利な方法で、モジュール性を備えている。 |
|||
* {{仮リンク|一意型|en|Uniqueness type}} - 関数型言語で副作用を扱う別の手法。 |
|||
== 外部リンク == |
|||
{{Wikibooks|Haskell|Understanding monads}} |
|||
===Haskellのモナドチュートリアル === |
|||
* [http://haskell.org/haskellwiki/Monad_tutorials_timeline Monad Tutorials Timeline] Probably the most comprehensive collection of links to monad tutorials, ordered by date. |
|||
*{{cite web|url=http://blog.sigfpe.com/2006/08/you-could-have-invented-monads-and.html|title=You Could Have Invented Monads! (And Maybe You Already Have.)|last=Piponi|first=Dan|date=August 7, 2006|work=A Neighborhood of Infinity|accessdate=2015-02-21}} — The most famous "blog post" tutorial. |
|||
*{{cite journal|last=Yorgey|first=Brent|date=12 March 2009|title=The Typeclassopedia|journal=The Monad.Reader|issue=13|pages=17–68|url=http://www.haskell.org/wikiupload/8/85/TMR-Issue13.pdf}} — An attempt to explain all of the leading typeclasses in Haskell in an elementary way, with monadic functors considered as only one form, best understood by comparison with others: e.g., the more general idea of a "Functor" as something you can map over; "Applicative" functors, and so forth; contains an extensive bibliography. |
|||
*{{cite web|url=https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/|title=Abstraction, intuition, and the "monad tutorial fallacy"|last=Yorgey|first=Brent|date=January 12, 2009|work=blog :: Brent -> [String]|publisher=[[WordPress.com]]|accessdate=2015-02-21}} — Opposes the idea of making a tutorial about monads in particular. |
|||
* [http://haskell.org/haskellwiki/What_a_Monad_is_not What a Monad is not] deals with common misconceptions and oversimplifications in a humorous way. |
|||
*{{cite web|url=https://noordering.wordpress.com/2009/03/31/how-you-shouldnt-use-monad/|title=How you should(n’t) use Monad|date=March 31, 2009|work=No Ordering|author= beelsebob |publisher=[[WordPress.com]]|accessdate=2015-02-21}} — Takes a similar point of view, locating monads in a much wider array of Haskell functor classes, of use only in special circumstances. |
|||
*{{cite web|url=http://mvanier.livejournal.com/3917.html|title=Yet Another Monad Tutorial (part 1: basics)|last=Vanier|first=Mike|date=July 25, 2010|work=Mike's World-O-Programming|publisher=[[LiveJournal]]|accessdate=2015-02-21}} — An extremely detailed set of tutorials, deriving monads from first principles. |
|||
*{{cite web|url=http://learnyouahaskell.com/a-fistful-of-monads|title=A Fistful of Monads|accessdate=2015-02-21}} An explanation of Monads, building on the concepts of Functors, Applicative Functors and Monoids discussed in the [http://learnyouahaskell.com/functors-applicative-functors-and-monoids previous chapter]. |
|||
* [http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html Functors, Applicatives and Monads in Pictures]. A humorous beginner's guide to monads. |
|||
==== 古いチュートリアル ==== |
|||
* [http://www.haskell.org/haskellwiki/All_About_Monads All About Monads] |
|||
* [http://haskell.org/haskellwiki/Monads_as_computation Haskell Wiki: Monads as Computation] |
|||
* [http://haskell.org/haskellwiki/Monads_as_containers Haskell Wiki: Monads as Containers] |
|||
*{{cite web|url=http://www.engr.mun.ca/~theo/Misc/haskell_and_monads.htm|title=Monads for the Working Haskell Programmer|last=Norvell|first=Theodore|publisher=Memorial University of Newfoundland|accessdate=2015-02-21}} |
|||
*{{cite journal|last=Klinger|first=Stefan|date=15 December 2005|title=The Haskell Programmer’s Guide to the IO Monad — Don’t Panic|publisher=Centre for Telematics and Information Technology, University of Twente|issue=5–54|issn=1381-3625|url=http://stefan-klinger.de/files/monadGuide.pdf}} |
|||
*{{cite web|url=http://onlamp.com/pub/a/onlamp/2007/08/02/introduction-to-haskell-pure-functions.html|title=Introduction to Haskell, Part 3: Monads|last=Turoff|first=Adam|date=August 2, 2007|work=ONLamp|publisher=O'Reilly Media|accessdate=2015-02-21}} |
|||
* [http://spbhug.folding-maps.org/wiki/MonadsEn Monads] A monad tutorial providing examples of non-trivial monads apart from the conventional IO/Maybe/List/State monads. |
|||
==== 他の文書 ==== |
|||
*{{cite web|url=http://members.chello.nl/hjgtuyl/tourdemonad.html|title=A tour of the Haskell monad functions|last=van Tuyl|first=Henk-Jan|date=2010-02-27|accessdate=2015-02-21}} |
|||
*{{cite journal|last=Moggi|first=Eugenio|title=Notions of computation and monads|url=http://www.disi.unige.it/person/MoggiE/ftp/ic91.pdf}} — The original paper suggesting use of monads for programming |
|||
*{{cite journal|last=Wadler|first=Philip|authorlink=Philip Wadler|date=August 2001|title=Monads for functional programming|publisher=University of Glasgow|url=http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf}} — Describes monads in Haskell (before they were implemented) |
|||
===Scala のモナドチュートリアル === |
|||
*{{cite video |people=League, Chris |date=July 12, 2010 |title=Monadologie: Professional Help for Type Anxiety |url=http://vimeo.com/13304075 |format=flv |medium=Tech talk |publisher=New York Scala Enthusiasts |location=[[ニューヨーク|New York City]]}} |
|||
*{{cite web|url=http://blog.tmorris.net/posts/understanding-monads-using-scala-part-1/index.html|title=Understanding Monads using Scala (Part 1)|last=Morris|first=Tony|date=June 22, 2010|work=λ Tony’s blog λ|accessdate=2015-02-21}} |
|||
* {{Cite web |
|||
| first1 = James |
|||
| last1 = Iry |
|||
| date = September 18, 2007 |
|||
| title = Monads are Elephants |
|||
| url = https://james-iry.blogspot.com/2007/09/monads-are-elephants-part-1.html |
|||
|accessdate=2015-02-21 |
|||
}} |
|||
* {{Cite web |
|||
| first1 = Gregory |
|||
| last1 = Meredith |
|||
| date = April 25, 2010 |
|||
| title = Pro Scala: Monadic Design Patterns for the Web |
|||
| publisher = |
|||
| edition = 1st |
|||
| pages = 300 |
|||
| isbn = |
|||
| url = https://github.com/leithaus/XTrace/blob/monadic/src/main/book/content/monadic.pdf |
|||
| postscript = <!--none--> |
|||
|accessdate=2015-02-21 |
|||
}} |
|||
=== 他の言語におけるモナド === |
|||
* [https://web.archive.org/web/20080515195640/http://sleepingsquirrel.org/monads/monads.html Monads in Perl] |
|||
* [http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/00introduction.html Monads in Ruby] |
|||
* [http://www.valuedlessons.com/2008/01/monads-in-python-with-nice-syntax.html Monads in Python] |
|||
* [http://lamp.epfl.ch/~emir/bqbase/2005/01/20/monad.html Monads in Scala] |
|||
* [https://web.archive.org/web/20131229232933/http://onclojure.com/2009/03/05/a-monad-tutorial-for-clojure-programmers-part-1/ Monads in Clojure] |
|||
* [http://labs.mudynamics.com/2009/05/13/monadic-parser-in-javascript/ Monads in JavaScript] |
|||
* [http://blogs.msdn.com/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx Introduction to monads in C# and LINQ] |
|||
* [https://github.com/louthy/csharp-monad Library of monads for C#] |
|||
* [http://blog.enfranchisedmind.com/2007/08/a-monad-tutorial-for-ocaml/ Monads in Ocaml] |
|||
* [http://blog.ircmaxell.com/2013/07/taking-monads-to-oop-php.html Monads in PHP] |
|||
{{DEFAULTSORT:もなと}} |
|||
[[Category:関数型プログラミング]] |
|||
[[Category:圏論]] |
2024年8月21日 (水) 17:32時点における最新版
関数型プログラミングにおいて、モナドはプログラムを構造化するための汎用的な抽象概念である。対応したプログラム言語では、ボイラープレート的なコードでもモナドを使って除去することが可能となる。これはモナドが、特定の形をした計算を表すデータ型と、それに関する生成と合成の2つの手続きを提供することによって実現されている。生成は任意の基本型の値をモナドに包んでモナド値を生成する手続きであり、合成はモナド値を返す関数(モナド関数)たちを合成する手続きである。[1]
広い範囲の問題をモナドを使うことで単純化できる。例えば、Maybe
モナドを使えば未定義値への対処が簡単になり、List
モナドを使えばリストに入った値を柔軟に扱うことができる。複雑に組み合わさった関数は、モナドを使えば、補助データの管理や制御構造や副作用を除去した簡単なパイプライン構造に置き換えることができる[1][2]。
モナドの概念や用語は圏論から来ている。圏論においては、モナドは追加の構造を持つ関手として定義されている[注釈 1]。1980年代の後半に研究され始め、1990年代前半には、一見無関係な計算機科学上の問題がモナドによって統一的で関数的にモデル化できることが分かってきた。圏論はモナドの満たすべき形式的な条件を与え、これはモナド則と呼ばれている。モナド則はモナド的なコードの形式的検証にも使用可能である。[3][4]
ある種の計算では、その計算の意味をモナドで明確に表現できることを利用して、プログラム言語の便利機能の実装にモナドを使うことができる。Haskellのように、標準ライブラリですでに汎用のモナド構造といくつかのインスタンスを定義している言語もある[1][5]。
歴史
[編集]プログラミングにおけるモナドの概念は1980年代のプログラム言語Opal (プログラミング言語)に見られるが、このときは「コマンド」と呼ばれ形式化はされなかった[要出典]。
1991年にEugenio Moggiはプログラムの構造化でモナドを初めて汎用的に使用した[6]。この成果を元に、Philip WadlerやSimon Peyton Jonesといったプログラム言語の研究者(二人ともHaskellの言語仕様に取り組んでいた)により実装が行われた。初期のバージョンのHaskellは問題の多い「遅延リスト」を用いた入出力を使っていたが、Haskell 1.3からは遅延評価により柔軟に合成可能なモナドによる入出力が導入された。
入出力に加えて、プログラム言語研究者とHaskellのライブラリ設計者は構文解析器やインタープリタにモナドを適用することに成功した。Haskellのdo記法に現れる性質から、モナドの概念はapplicative functorとarrowへと一般化された。
長い間、Haskellとその派生だけがプログラミングにおけるモナドの主な利用者であった。最近では、F#がコンピュテーション式または「ワークフロー」と呼ばれる機能を導入した。これは、命令型プログラムしか経験のないプログラマにもなじみやすい構文を使ったモナド的構成を提供しようという試みである[7]。
動機付けと例
[編集]プログラム言語Haskellはモナドを多用する関数型の言語であり、簡単にモナドの合成ができるような構文糖衣を持っている。特に指定がない限り、この記事のコード例はHaskellで書かれている。
モナドの紹介として一般的なふたつの例 Maybe モナドと I/O モナドを取り上げる。もちろん、モナドはHaskellに限られたものではないので、JavaScriptでの Writer モナドの例もその次に扱う。
Maybeモナド
[編集]オプション型 Maybe a を考えよう。これは型 a の値をひとつ持つか、全く持たないかのふたつの状態を表現する型である。これらを区別するために、二種類の代数的データ型構成子を持つ。Just t
はひとつの値t
を保持し、Nothing
は値を何も保持しない。
data Maybe t = Just t | Nothing
この型を使って検査例外の簡単なものを作ってみたいとする。計算の途中で失敗した場合は、残りの計算は飛ばして結果Nothing
を返し、すべての計算が成功した場合は何か値x
を保持したJust x
を返すことにする。
以下の例では、add
はMaybe Int型のふたつの引数を受け取って、同じ型の結果を返す。mx
とmy
がともにJust
である場合は、これらの値の和をJust
に入れて返したい。もし、mx
とmy
のどちらかがNone
だった場合は、Nothing
を返したい。こういった振る舞いをする関数を単純に実装しようとしたら、「if Nothing
then Nothing
else Just x
のx
に何かする」の形の場合わけを複数ネストすることになり、すぐやっかいなものになる[1]。
add :: Maybe Int -> Maybe Int -> Maybe Int
add mx my =
case mx of
Nothing -> Nothing
Just x -> case my of
Nothing -> Nothing
Just y -> Just (x + y)
これを楽にするための、各計算を繋げる演算子を定義することができる。二項演算子 bind (>>=
) は失敗する可能性のある計算の結果を、もうひとつの失敗する可能性のある計算へ連鎖する。最初の引数が Nothing
だった場合は、二番目の引数(関数引数)は無視されて、単に計算全体が失敗する。もし、最初の引数が Just x
だった場合は、この値 x
は二番目の関数引数に渡されて、この関数の結果として新しい Maybe の値が返される。これは Just
な値か失敗かのどちらかになる。
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= _ = Nothing -- 失敗した計算は Nothing を返す
(Just x) >>= f = f x -- 値 x に関数 f を適用する
計算の状態に影響を与えずに値を投入する構築子は、Just
がすでにそうなっている。
return :: a -> Maybe a
return x = Just x -- 値 x を包んで、型 (Maybe a) の値として返す
これらを使うことで、上の例は次のように書ける。
add :: Maybe Int -> Maybe Int -> Maybe Int
add mx my = -- (Maybe Int) 型のふたつの値を足す関数。各入力は Nothing の場合がある
mx >>= (\x -> -- mx が Nothing でない場合に、値 x を抽出する
my >>= (\y -> -- my が Nothing でない場合に、値 y を抽出する
return (x + y))) -- 値(x+y)を包んで、(Maybe Int)型の値として和を返す
さらにdo記法という構文糖衣を使うことで、次のように書くことができる。
add :: Maybe Int -> Maybe Int -> Maybe Int
add mx my = do
x <- mx
y <- my
return (x + y)
この種の操作は頻出なので、Haskellの標準関数(liftM2
)が用意されている。これはふたつのモナド的な値(ここでは、ふたつのMaybe)と、その中身(ふたつの数値)を組み合わせる関数(加算)を受け取る関数であり、上の例は次のように書ける。
add :: Maybe Int -> Maybe Int -> Maybe Int
add = liftM2 (+)
(上記のdo記法を使ってliftM2の定義を書いてみよう)
I/Oモナド
[編集]Haskellのような純粋関数型言語では関数が外部から観測可能な作用をもつことは、関数の意味から外れることになるため許されない。しかし、関数が直接副作用を起こすのではなく、求める副作用を記述する値を構築して返し、呼び出した側が適当な時に実行することは可能である[8]。Haskellの記法では、型 IO a の値がこのようなアクションを表現していて、実行されたときには、型 a の何らかの値を生成する。
IO
型の値は「現在の世界の状態を引数に取り、関数の返り値として変化した状態を持つ新しい世界を返す」という意味でアクションとみなすことができる。例えば、関数 doesFileExist
と removeFile
は次の型を持つ関数である。
doesFileExist :: FilePath -> IO Bool
removeFile :: FilePath -> IO ()
removeFile
は関数としては、FilePath
を引数にとり、ある IO
アクションを返す。このアクションが実行されると世界を、この場合はファイルシステムを FilePath
という名前のファイルの存在しないものに変更する。この場合は、IO
の持っている値は ()
型の値なので、なにも結果は生成されず、呼び出し側はこれを気にする必要はない。一方で、doesFileExist
の場合は、関数の返す IO
アクションはブール型の値、True
または False
を包んでいて、概念的には、アクションが実行されたときのファイルシステムに FilePath
という名前のファイルが存在したかどうかを呼び出し側が知っているという世界の状態を表現している。アクションからアクションへと世界の状態を渡して管理するこの方法により、決まった順序で状態を変化させていくアクションの列を定めることが可能となる。この過程は時相論理において宣言的な命題のみで時間の経過を表現する手法と似ている。以下の例ではプログラム内のアクションを連鎖する方法の詳細を再び Haskell を使って明らかにする。
基本的な種類の入出力操作、標準出力に文字列を書く、標準入力から文字列を読む、ファイルの読み書き、ネットワーク越しのデータ送信等をすべて記述できるようにしたい。さらに、これらのプリミティブを組み合わせて大きなプログラムを作れる必要もある。例えば次のように書けることが望ましい。
main :: IO ()
main = do
putStrLn "What is your name?"
name <- getLine
putStrLn ("Nice to meet you, " ++ name ++ "!")
この直観的な記法はどのように形式化できるだろうか?このためには各入出力アクションに対して、いくつかの基本的な操作を実行できる必要がある。
- ふたつの入出力アクションをひとつにできなければならない。Haskellでは、中置演算子
>>
を用いて、putStrLn "abc" >> putStrLn "def"
と書き、コンソールに二行の文字列を表示するひとつのアクションである。演算子>>
の型は IO a → IO b → IO b であり、ふたつの入出力アクションを引数に取り、順番に実行するひとつのアクションにまとめて返す。 - 何もしないという入出力アクションも必要である。これは値を返すがなんの副作用も持たない。Haskellではこのアクションは
return
で構築され、この型は a → IO a である。 - さらに、最初のアクションの結果に基づいて次に実行するアクションを決める必要がある場合がある。Haskellでは、演算子
>>=
(bindと読む)を使い、型は IO a → (a → IO b) → IO b である。左側のオペランドは型a
の値を返す入出力アクションであり、右側のオペランドは左側のアクションの生成した値に応じて次に実行する入出力アクションを選択する関数である。結果は、最初のアクションを実行しその値を関数に渡した評価結果をアクションとして実行し、最後にこのアクションの結果を返す合成されたひとつのアクションとなる。
- Haskellのこの演算子を使った例は
getLine >>= putStrLn
であり、これは標準入力から一行文字列を読み込み、それをそのまま標準出力に書き出す。最初の演算子>>
は単にこの演算子の特別な場合である。最初のアクションの結果は無視され、二番目のアクションは常に同じである。
これらの3つの演算子をプリミティブな入出力操作として「いかなるプログラムでも」書けるかというのは、データ変換(ラムダ式使う)やif式やループを含めたとしても自明であるとは言いがたい。上の例は長い式を使って次のように書ける。
main =
putStrLn "What is your name?" >>
getLine >>= \name ->
putStrLn ("Nice to meet you, " ++ name ++ "!")
bind 演算子の持つパイプライン構造により、getLine と putStrLn の命令はそれぞれ一度だけ、書いた順序で評価され、入力ストリームから文字列を取り出して、出力ストリームに出力する副作用は関数型パイプラインの中でも正しく実行される。これは言語が関数をアウト・オブ・オーダー実行や遅延評価する場合でも成立する。
明らかに入出力の定義とMaybeの定義は共通の構造を持っているが、異なっている部分も多い。上述した構造や他のよくある似たような多くの構造の抽象化である。モナドの一般化された概念は、計算の一部が副作用を発生させるにもかかわらず、純粋関数的な計算として実行したくなるようなあらゆる状況を包含している。
形式的定義
[編集]モナドは、元となる型システムが与えられたとき、その内部に対応する型システム(モナド的型システムと呼ぶ)を埋め込む構成である(つまり、各モナド的型は元のシステムの型でもある)。このモナド的型システムは元の型システムの重要な点は全て保存するが、モナドに固有の特徴を追加する[注釈 2]。
モナドは複数の方法で定義できる。そのうちの1つは「モナドとは、ある法則(モナド則)を満たす三つ組みである」[9] で表される定義である。すなわち圏論のクライスリ圏におけるクライスリトリプルである。
三つ組
[編集]三つ組は以下の要素からなる。
名前 | 別名 | 定義式 | 説明 |
---|---|---|---|
型構築子M | 型 x から派生した別の型を得る(型から型を作る)演算子
| ||
unit関数 | return | unit :: x -> M x
|
型 x の値を型 M x の値へうつす多相な関数
|
bind関数 | >>=
|
bind :: M x -> (x -> M y) -> M y
|
型 M x の値、型 x の値を型 M y の値へ写す関数の2つから M y を得る関数
|
型構築子M
[編集]class Monad M where
...
value :: M x
型構築子Mは、ある型 x
から派生した型を得る方法を定める[10]。得られる型はしばしば M x
で表記される。
unit 関数
[編集]unit関数( return
とも[注釈 3])は型 x
の引数を型 M x
の返り値にうつす関数である。すなわちunit 関数は型 x
の値をモナド型 M x
の値へうつす多相な関数である。引数値変換の有無は定義されない(一般には値を変換せずに保持する(例:入力 1
=>出力 Maybe(1)
)。
bind 関数
[編集]bind関数( >>=
とも[11])は「モナド的値」と「値をモナド的値へうつす関数」を引数にとり、第1引数のモナド的値を第2引数関数の値域へうつす。
モナド則
[編集]三つ組 {M, unit(return), bind(>>=)} をモナドと成す規則をモナド則という[12]。以下がモナド則である。
- return は >>= に対して、ほとんど単位元である。
(return x) >>= f ≡ f x
m >>= return ≡ m
- ふたつの関数を続けて bind するのは、これらの関数から決まるひとつの関数を bind することに等しい。
(m >>= f) >>= g ≡ m >>= ( \x -> (f x >>= g) )
公理を do 記法で表現することもできる。
do { f x } ≡ do { v <- return x; f v }
do { m } ≡ do { v <- m; return v }
do { x <- m; y <- f x; g y } ≡ do { y <- do { x <- m; f x }; g y }
また、モナド合成演算子
(f >=> g) x = (f x) >>= g
を使うと
return >=> g ≡ g
f >=> return ≡ f
(f >=> g) >=> h ≡ f >=> (g >=> h)
fmap と join
[編集]Haskellではモナドを return と bind 関数を用いて定義するが、return にふたつの演算子 join と fmap を加えることでも定義可能である。この定式化は圏論における元のモナドの定義により近くなる。fmap 演算子は型 (t→u) → M t→M u[13] を持ち、ふたつの型間の関数を受け取って、モナドの中の値に「同じこと」をする関数を返す。join 演算子は型 M (M t)→M t を持ち、二層になったモナド的な情報を平らにしてひとつにする。
ふたつの定式化は以下の関係を持つ。
fmap f m = m >>= (return . f)
join n = n >>= id
m >>= g ≡ join (fmap g m)
ここで、m は M t、n は M (M r)、f は t → u、g は t → M v の型を持つ。t、r、u、v は元の型である。
モナドでなくても、型と関数からなる圏では fmap 関数は任意の函手に対して定義できる。函手は次の法則を満たす。
fmap id ≡ id
fmap (f . g) ≡ (fmap f) . (fmap g)
同じ圏で、return は基点付き函手を特徴付ける。これは値を函手の中に「持ち上げる」ことによる。満たすべき法則は次の通りである。
return . f ≡ fmap f . return
さらに、join によってモナドが特徴付けられる。
join . fmap join ≡ join . join
join . fmap return ≡ join . return = id
join . fmap (fmap f) ≡ fmap f . join
加法的モナド
[編集]加法的モナドは、モノイド則を満たす二項演算子 mplusとその単位元としてモナド的な値 mzero を備えたモナドである。演算子 mplus の型は M t → M t → M t であり(ここで、M はモナドの構築子であり t は元の型である)、結合律と左右からの単位元律を満たす。
(a `mplus` b) `mplus` c = a `mplus` (b `mplus` c)
m `mplus` mzero ≡ mzero `mplus` m ≡ m
このことから、加法的モナドはモノイドでもある。>>=
に対しては、mzeroはヌル要素として振舞う。ゼロを掛けるとゼロになるように、mzero と関数を bind すると結果はゼロとなる。
mzero >>= f ≡ mzero
同様に、モナド的な値 m とつねにゼロを返す関数を bind すると、結果はゼロである。
m >>= (\x -> mzero) ≡ mzero
直観的には、モナド的値としてのゼロはモナドに関連した構造だけを持ち元の型の値を持たない。Maybe モナドでは、「Nothing」がゼロである。リストモナドでは、空リストがゼロである。
構文糖衣: do記法
[編集]bind 演算子 >>=
を直接使ってプログラムを書くのがよい場合もあるが、典型的にはdo記法 (OCamlでは perform記法、F#ではコンピュテーション式)を使用し、命令型言語のような見た目にできる。コンパイラはdo記法の式を >>=
を使ったものに変換する。例えば
a = do x <- [3..4]
[1..2]
return (x, 42)
を変換した結果は以下となる。
a = [3..4] >>= (\x -> [1..2] >>= (\_ -> return (x, 42)))
リストモナドの実装を見てみよう。リストにマップを行い結合を行う(平らにする)この関数は concatMap とも呼ばれる。
instance Monad [] where
m >>= f = concat (map f m)
return x = [x]
fail s = []
よって、以下のような変形が行われて、それぞれの式はすべて等価である。
a = [3..4] >>= (\x -> [1..2] >>= (\_ -> return (x, 42)))
a = [3..4] >>= (\x -> concatMap (\_ -> return (x, 42)) [1..2] )
a = [3..4] >>= (\x -> [(x,42),(x,42)] )
a = concatMap (\x -> [(x,42),(x,42)] ) [3..4]
a = [(3,42),(3,42),(4,42),(4,42)]
リスト[1..2]は使われないことに注意しよう。左矢印をつけないことで、連鎖した関数は引数を無視するように変換されて、値ではなくモナド的な構造だけが問題になることを示すことができる。例えば、状態モナドで値を生成せずに状態だけを変化させるようなときに使われる。do記法は >>=
の単なる構文糖衣であるので、どんなモナドに対しても使うことができる。
Maybeモナドの値を安全に割り算する以下の定義も等価である。
x // y = do
a <- x -- xとyの中に値がある場合は取り出す
b <- y
if b == 0 then Nothing else Just (a / b)
x // y = x >>= (\a -> y >>= (\b -> if b == 0 then Nothing else Just (a / b)))
同様にF#ではコンピュテーション式を使うことができる。
let readNum () =
let s = Console.ReadLine()
let succ,v = Int32.TryParse(s)
if (succ) then Some(v) else None
let secure_div =
maybe {
let! x = readNum()
let! y = readNum()
if (y = 0)
then None
else return (x / y)
}
このmaybeブロックは構文糖衣であり、以下の式に変換される。
maybe.Delay(fun () ->
maybe.Bind(readNum(), fun x ->
maybe.Bind(readNum(), fun y ->
if (y=0) then None else maybe.Return(x / y))))
モナド的な汎用関数
[編集]安全な割り算の結果は、値が Nothing であるかを素直に確認せずに(つまり割り算の結果があるかを確認せずに)そのまま計算に使えたほうが便利なことがある。これは「持ち上げ」関数を使うことで可能である。この関数は Maybe に限定されず、任意のモナドに対して定義される。Haskellでは LiftM2 と呼ばれる。
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM2 op mx my = do
x <- mx
y <- my
return (op x y)
関数型を示す矢印が右結合であることから、liftM2は2引数の関数を引数に取って、さらなる2引数の関数を返す。型シグネチャは m がモナドのとき、任意の2引数関数を「持ち上げ」ることができることを示している。例えば、
(.*.) :: (Monad m, Num a) => m a -> m a -> m a
x .*. y = liftM2 (*) x y
は演算子 (.*.) を定義し、それは引数がどちらも Nothing でないときに、掛け算した結果を返す(どちらかが Nothing の場合は Nothing を返す)。この方法の利点はモナドの実装に深入りする必要がないことである。他の同じような関数やモナドを使う場合でも、liftM2 を使うことで、やりたいことが鮮明になる(コードの再利用を見よ)。
数学的には、liftM2 は次のように定義される。
他の例
[編集]恒等モナド
[編集]もっとも単純なモナドは恒等モナドであり、値になんの情報も付加しない。
Id t = t
return x = x
x >>= f = f x
このモナドのdoブロックは変数の代入を実行する。例えば、do {x <- 2; return (3*x)} の結果は6である。
圏論的な見方をすると、恒等モナドは恒等函手の間の随伴から得られる。
コレクション
[編集]リストや集合や多重集合といった、おなじみのコレクション型にもモナドであるものがある。リストの場合の定義は次のようになる。
-- "return" constructs a one-item list.
return x = [x]
-- "bind" concatenates the lists obtained by applying f to each item in list xs.
xs >>= f = concat (map f xs)
-- The zero object is an empty list.
mzero = []
リスト内包記法はリストモナドに固有のもので、例えば、リスト内包 [ 2*x | x <- [1..n], isOkay x] はリストモナドでの計算 do {x <- [1..n]; if isOkay x then return (2*x) else mzero;} に対応している。
リスト内包記法は集合の内包記法に似ているが、集合はモナドにならない。なぜなら、計算の型は等しいかどうかを比較可能でなければならない — モナドは計算の型についてこれを課していないように見えるにもかかわらず — という制限があるからである。実際、集合は制限されたモナド(restricted monad)である[14]。コレクションによるモナドは非決定計算を自然に表現する。リスト(または他のコレクション)は非決定的な計算の異なった実行パスが今の時点で取りうる結果の全体を表す。例えば、x <- [1,2,3,4,5] を実行したとすると、変数 x はこのリストから非決定的に選ばれた値であるとみなしている。xを返すことは、各実行パスの結果を値のリストにまとめることを意味する。さらに、bind 演算子では今の可能な結果に対して関数 f を実行し、それぞれの結果をひとつのリストに結合する。
if condition x y then return () else mzeroの形の文はよく現れる。条件が真である場合は何もしない計算を非決定的に選択し、意味のある値は返さない。一方で、条件が偽の場合は、選択肢の存在しない非決定な値 mzero = [] を返す。つまり、この実行パスを終了し、他の実行パスは走り続ける。これは条件を満たす特定の実行パスだけを通す「ガード」の役割を果たす。よって、コレクションモナドは論理パズルや数独などの問題を解く場合に役に立つ。
Haskellのように遅延評価を行う言語では、リストはその要素が必要になって初めて評価される。例えばリストの最初の要素を要求した場合は、最初の要素だけが計算される。非決定計算にリストモナドを使用した場合には、全ての計算からなる結果は非決定的に遅延リストとして生成され、最初の要素を要求すると、必要最小限の計算のみが行われて、最初の結果を返す。この過程はバックトラックに対応している。ある実行パスにいて計算に失敗した(mzeroに評価された)場合、最後に行った分岐までバックトラックを行い、そこから次のパスを選んで実行を続ける。二番目の要素が要求された場合、再びその答を得るのに必要な最小限の計算が行われる。よって、遅延評価の言語ではリストモナドを使って簡単にバックトラックを実装することができる。
圏論的な見方をすると、コレクションモナドは集合の圏とモノイドの圏の間の自由函手と忘却函手の随伴から作られる。
コレクションの種類 | モノイドの種類 |
---|---|
リスト | モノイド |
有限多重集合 | 可換モノイド |
有限集合 | 冪等可換モノイド |
finite permutation | 冪等非可換モノイド |
状態モナド
[編集]状態モナドは状態を付加した計算の型である。好きな型に対して、その型を状態として持つ状態モナドを作ることができる。これは状態を引数に取り、通常の返り値(型 t の値)と新しい状態(型 s の値)を返す関数である。
type State s t = s -> (t, s)
今までに見たモナドと違い、状態の型という型引数を取ることに注意しよう。モナドの演算は次のように定義される。
-- "return" は状態を更新せず、受け取った値を生成する
return x = \s -> (x, s)
-- "bind" は m が変更した状態を f に渡して結果とする
m >>= f = \r -> let (x, s) = m r in (f x) s
状態を操作する関数を挙げる。
get = \s -> (s, s) -- 現時点での状態を見る
put s = \_ -> ((), s) -- 状態を上書きする
modify f = \s -> ((), f s) -- 状態を更新する
初期状態を指定して状態モナドを実行する関数。
runState :: State s a -> s -> (a, s)
runState t s = t s
状態モナドのdoブロック内の計算は状態の確認や更新を行うことができる。
非形式的に書くと、状態の型 S を持つ状態モナドは返り値の型 T を関数型 に変換する。return は単に
であり、bind は
である。
圏論的な見方では、状態モナドはデカルト閉圏の定義に現れる積函手と指数函手の随伴から作られる。
環境モナド
[編集]環境モナド(Readerモナドや関数モナドとも呼ばれる)は環境を共有した計算を可能にする。型構築子は型 T を 関数型 E → T にうつす。ここで、E は共有したい環境の型である。モナド演算子は
で定義される。
環境を操作する関数として
がある。ask は現在の環境を取得するのに使う。localは指定した計算を一時的に変更した環境で行う。状態モナドと同様に、環境モナドによる計算は単に環境の値にモナドの値を適用することで実行できる。
Writerモナド
[編集]Writerモナドは本来の計算を行いながら、様々な種類の追加の出力を作成することができる計算である。ログ取りやプロファイリングに有用である。元の型 T に対して、Writerモナドは型 W × T の値を持つ。ここで、W はモノイド則を満たす演算を持った型である。すなわち、W は二項演算子 (a,b) → a*b と単位元 ε を持っており、これは結合則 (a*b)*c = a*(b*c) を満たし、任意の x について、x*ε = ε*x = x を満たす。
モナド演算は
で定義される。ここで、εと * はモノイド W の単位元と二項演算子である。
JavaScriptではWriterモナドの値として2要素の配列を使うことができる。最初の要素は任意の値であり、二番目の要素は出力を貯めておくための配列である。
var writer = [ value, [] ];
角括弧はこのモナドの値コンストラクタとして使っていて、より単純な要素(配列の0番目は値、1番目にはログ配列)からWriterモナドの値を作っている。
unit (return はJavaScriptの予約語なのでこちらを使う)は元の値に空のログ配列をつけるだけである。
var unit = function(value) { return [ value, [] ]; };
bind は最初のWriterモナドの中の値に関数を適用し、それぞれのログ配列を結合して新しいモナドの値として返す。
var bind = function(writer, transform) {
var value = writer[0];
var log = writer[1];
var result = transform(value);
return [ result[0], log.concat(result[1]) ];
};
pipelineは補助関数で関数の配列を順番に bind する。
var pipeline = function(writer, transforms) {
var result = writer;
transforms.forEach(function (transform) {
result = bind(result, transform);
});
return result;
};
モナドの値を返す関数の例を挙げる。
var squared = function(x) {
return [ x * x, 'was squared.' ];
};
var halved = function(x) {
return [ x / 2, 'was halved.' ];
};
最後に数学関数のパイプラインにデバッグ情報(デバッグ情報の配列は結合されて、値と一緒に返される)を追加する例を挙げる。
pipeline(unit(4), [ squared, halved ]); // [ 8, [ 'was squared.', 'was halved.' ] ]
継続モナド
[編集]結果の型 の継続モナドは型 を関数型 にうつす。これは継続渡しスタイルのモデルとして使われる。return と bind 関数の定義は
である。
call-with-current-continuation関数は次で定義できる。
他の例
[編集]研究者により発見されたモナドになる概念を挙げる。
自由モナド
[編集]自由モナドは自由モノイドと似ていて、直観的には、使う型に依存せずにモナド則(またはモノイド則)を満たすようにできる汎用の構造である。
型 t に対して、t
の自由モノイドは [t]
であり、結合的な二項演算子として ++
、単位元として []
を選ぶ。Haskellでは以下のように書ける。
instance Functor [] where
fmap _ [] = []
fmap fun (x:xs) = fun x : fmap fun xs
instance Monoid [t] where
mappend xs ys = xs ++ ys
mempty = []
具体的なモノイドとでは、値 t1,t2,...,tn
を二項演算で足すことができるが、[]
では単に、結合 [t1,t2,...,tn]
になるだけであり、単に「一緒に集めた」だけである。ここで、「一緒に集めた」結果がどうなるべきかは定めない。
自由モナドも同じ考えに基づいて定義される。上の定義 List t = Nil | Cons t (List t)
に函手を挿入することで、次の定義を得る。
data Free f a = Return a | Bind (f (Free f a))
instance Functor f => Functor (Free f) where
fmap fun (Return x) = Return (fun x)
fmap fun (Bind x) = Bind (fmap (fmap fun) x)
instance Functor f => Monad (Free f) where
return x = Return x
x >>= fun = Bind (fmap (>>= fun) x)
List
が値のリストを保存するのと違って、Free
はドメインの値を決めた函手のリストを保存する。Free
の Functor
と Monad
インスタンスの定義によると、受け取った関数を fmap
でリストに落としているだけしかしていないことが分かる。
コモナド
[編集]コモナドはモナドの圏論的な双対である。型構築子 W T と2つの演算 extract と extend を持つ。extract の型は W T → T であり、extend の型は (W T → T' ) → W T → W T' であり、以下のコモナド則を満たすとする。
他の方法として、fmap と extract と duplicate を使った定義もできる。fmap と extract により W は余基点付き函手となる。duplicate がコモナドを特徴付ける。duplicate の型は W T → W (W T) であり、次の規則を満たす。
ふたつの定式化の関係は
で与えられる。
モナドが副作用を表現すると言われているのに対して、コモナド W は一種の「文脈」を表している。extract は文脈から値を取り出す関数である。extend は型 W A → B を持つ「文脈依存の関数」を合成してパイプラインを作る。
恒等コモナド
[編集]恒等コモナドはもっとも単純なコモナドであり、型 T をそれ自身にうつす。extractは恒等関数であり、extendは関数適用である。
積コモナド
[編集]積コモナドは型 を積の型 にうつす。ここで はコモナドの文脈の型である。コモナド演算は
で定める。
関数コモナド
[編集]関数コモナドは型 を関数型 にうつす。ここで、 はモノイド構造を持つ型である。コモナド演算は
で定める。εと * はモノイド の単位元と二項演算子である。
余状態コモナド
[編集]余状態コモナドは型 を型 にうつす。ここで S はストアの型である。コモナド演算は
で定義される。
脚注
[編集]注釈
[編集]出典
[編集]- ^ a b c d O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). “Monads”. Real World Haskell. Sebastopol, California: O'Reilly Media. chapter 14. ISBN 978-0596514983
- ^ Wadler, Philip (June 1990). Comprehending Monads. ACM Conference on LISP and Functional Programming. Nice, France. CiteSeerX 10.1.1.33.5381。
- ^ a b Moggi, Eugenio (1991). “Notions of computation and monads”. Information and Computation 93 (1): 55–92. doi:10.1016/0890-5401(91)90052-4 .
- ^ Wadler, Philip (January 1992). The essence of functional programming. 19th Annual ACM Symposium on Principles of Programming Languages. Albuquerque, New Mexico. CiteSeerX 10.1.1.38.9516。
- ^ Hudak, Paul; Peterson, John; Fasel, Joseph (1999). “About Monads”. A Gentle Introduction to Haskell 98. chapter 9
- ^ Moggi, Eugenio (1991). “Notions of computation and monads”. Information and Computation 93 (1). doi:10.1016/0890-5401(91)90052-4 .
- ^ “Some Details on F# Computation Expressions”. 2007年12月14日閲覧。
- ^ Peyton Jones, Simon L.; Wadler, Philip. Imperative Functional Programming. Conference record of the Twentieth Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, Charleston, South Carolina. 1993
- ^ A monad is a triple (M, unit,>>=) consisting of a type constructor M and two operations of the following types Tomas Petricek (2018) What we talk about when we talk about monads. The Art, Science, and Engineering of Programming, 2018, Vol. 2, Issue 3, Article 12. [1]
- ^ The monad type constructor
m
is added to function results (modulo currying) and nowhere else. Hackage. Control.Monad [2] - ^ The >>= operator is known as bind Tomas Petricek (2018) What we talk about when we talk about monads. The Art, Science, and Engineering of Programming, 2018, Vol. 2, Issue 3, Article 12. [3]
- ^ “Monad laws”. HaskellWiki. haskell.org. 2011年12月11日閲覧。
- ^ “Functors, Applicative Functors and Monoids”. learnyouahaskell.com. 2015年2月21日閲覧。
- ^ How to make Data.Set a monad shows an implementation of the Set restricted monad in Haskell
関連項目
[編集]- アロー (関数型プログラミング) - モナドが計算の結果に作用を追加するのに対して、アローは入力を同様に一般化する。
- アスペクト指向プログラミング - 二次的であったりサポート用の機能を分離してモジュール性を上げるパラダイムである。
- Effect system - 型で副作用を記述する異なった手法。
- 制御の反転 - 再利用可能なソフトウェアから特定の関数を呼ぶ際の抽象的な原則である。
- モナド変換子 - モナドを合成する便利な方法で、モジュール性を備えている。
- 一意型 - 関数型言語で副作用を扱う別の手法。
外部リンク
[編集]Haskellのモナドチュートリアル
[編集]- Monad Tutorials Timeline Probably the most comprehensive collection of links to monad tutorials, ordered by date.
- Piponi, Dan (August 7, 2006). “You Could Have Invented Monads! (And Maybe You Already Have.)”. A Neighborhood of Infinity. 2015年2月21日閲覧。 — The most famous "blog post" tutorial.
- Yorgey, Brent (12 March 2009). “The Typeclassopedia”. The Monad.Reader (13): 17–68 . — An attempt to explain all of the leading typeclasses in Haskell in an elementary way, with monadic functors considered as only one form, best understood by comparison with others: e.g., the more general idea of a "Functor" as something you can map over; "Applicative" functors, and so forth; contains an extensive bibliography.
- Yorgey, Brent (January 12, 2009). “Abstraction, intuition, and the "monad tutorial fallacy"”. blog :: Brent -> [String]. WordPress.com. 2015年2月21日閲覧。 — Opposes the idea of making a tutorial about monads in particular.
- What a Monad is not deals with common misconceptions and oversimplifications in a humorous way.
- beelsebob (March 31, 2009). “How you should(n’t) use Monad”. No Ordering. WordPress.com. 2015年2月21日閲覧。 — Takes a similar point of view, locating monads in a much wider array of Haskell functor classes, of use only in special circumstances.
- Vanier, Mike (July 25, 2010). “Yet Another Monad Tutorial (part 1: basics)”. Mike's World-O-Programming. LiveJournal. 2015年2月21日閲覧。 — An extremely detailed set of tutorials, deriving monads from first principles.
- “A Fistful of Monads”. 2015年2月21日閲覧。 An explanation of Monads, building on the concepts of Functors, Applicative Functors and Monoids discussed in the previous chapter.
- Functors, Applicatives and Monads in Pictures. A humorous beginner's guide to monads.
古いチュートリアル
[編集]- All About Monads
- Haskell Wiki: Monads as Computation
- Haskell Wiki: Monads as Containers
- Norvell, Theodore. “Monads for the Working Haskell Programmer”. Memorial University of Newfoundland. 2015年2月21日閲覧。
- Klinger, Stefan (15 December 2005). The Haskell Programmer’s Guide to the IO Monad — Don’t Panic. Centre for Telematics and Information Technology, University of Twente. ISSN 1381-3625 .
- Turoff, Adam (August 2, 2007). “Introduction to Haskell, Part 3: Monads”. ONLamp. O'Reilly Media. 2015年2月21日閲覧。
- Monads A monad tutorial providing examples of non-trivial monads apart from the conventional IO/Maybe/List/State monads.
他の文書
[編集]- van Tuyl, Henk-Jan (2010年2月27日). “A tour of the Haskell monad functions”. 2015年2月21日閲覧。
- Moggi, Eugenio. Notions of computation and monads . — The original paper suggesting use of monads for programming
- Wadler, Philip (August 2001). Monads for functional programming. University of Glasgow . — Describes monads in Haskell (before they were implemented)
Scala のモナドチュートリアル
[編集]- League, Chris (12 July 2010). Monadologie: Professional Help for Type Anxiety (flv) (Tech talk). New York City: New York Scala Enthusiasts.
- Morris, Tony (June 22, 2010). “Understanding Monads using Scala (Part 1)”. λ Tony’s blog λ. 2015年2月21日閲覧。
- “Monads are Elephants” (September 18, 2007). 2015年2月21日閲覧。
- “Pro Scala: Monadic Design Patterns for the Web”. pp. 300 (April 25, 2010). 2015年2月21日閲覧。