関数型プログラミングにおいて、モナドとは、プログラムフラグメント(関数)を結合し、その戻り値を追加の計算を伴う型でラップする構造である。ラッピングモナド型の定義に加えて、モナドは2つの演算子を定義する。1つは値をモナド型でラップし、もう1つはモナド型の値を出力する関数を合成する(これらはモナド関数と呼ばれる)。汎用言語は、モナドを使用して、一般的な操作に必要な定型コード(未定義値や誤りのある関数の処理、簿記コードのカプセル化など)を削減する。関数型言語は、モナドを使用して、複雑な関数のシーケンスを、制御フローと副作用を抽象化する簡潔なパイプラインに変換する。[1] [2]
モナドの概念と用語はどちらも元々は圏論に由来しており、圏論ではモナドは追加の構造を持つ関数として定義されています。 [a] 1980年代後半から1990年代初頭に始まった研究により、モナドは一見異なるコンピュータサイエンスの問題を統一された関数型モデルにまとめることができることが確立されました。圏論では、モナド則と呼ばれるいくつかの形式的な要件も提供されており、これはあらゆるモナドが満たすべき要件であり、モナドコードの検証に使用できます。[3] [4]
モナドはある種の計算の意味を明示的にするため、便利な言語機能を実装するためにも使用できます。Haskellなどの一部の言語では、一般的なモナド構造と共通インスタンスのコアライブラリにあらかじめ定義された定義を提供しています。[1] [5]
概要
「モナドの場合m、型の値はモナドのコンテキスト内でm a型の値にアクセスできることを意味します。」—CA McCann [6]a
より正確に言うと、シナリオに固有の理由により、値への無制限のアクセスが不適切である場合にモナドを使用できます。Maybe モナドの場合、値が存在しない可能性があるためです。IO モナドの場合、値がまだ不明である可能性があるためです。たとえば、モナドがプロンプトが表示された後にのみ提供されるユーザー入力を表す場合などです。すべての場合において、アクセスが意味をなすシナリオは、モナドに定義されたバインド操作によって捕捉されます。Maybe モナドの場合、値は存在する場合にのみバインドされ、IO モナドの場合、値はシーケンス内の前の操作が実行された後にのみバインドされます。
モナドは、型コンストラクタ Mと 2 つの操作を定義することによって作成できます。
return :: a -> M a(ユニットとも呼ばれる)は、型の値を受け取り、それを型のモナド値aにラップし、M abind :: (M a) -> (a -> M b) -> (M b)(通常は と表されます>>=) は、モナド値と、基本型 の値を受け入れるM a関数を受け取ります。Bind は をアンラップしてそれに適用し、 の結果をモナド値 として処理できます。faaffM b
join (演算子の代わりに関数を使用する、代替の同等な構成については、bind後のセクション§ 関数からの導出を参照してください。)
これらの要素を使用して、プログラマーは、式内で連結された複数のバインド演算子を使用して、関数呼び出しのシーケンス (「パイプライン」) を作成します。各関数呼び出しは、入力されたプレーン型の値を変換し、バインド演算子は返されたモナド値を処理します。この値は、シーケンスの次のステップに渡されます。
通常、bind 演算子>>=には、パラメーターとして受け取った関数では利用できない追加の計算ステップを実行する、モナドに固有のコードが含まれます。 合成された関数呼び出しの各ペアの間で、bind 演算子は、m a関数内でアクセスできない追加情報をモナド値に注入しf、パイプラインに渡すことができます。 また、特定の条件下でのみ関数を呼び出したり、関数呼び出しを特定の順序で実行したりするなど、実行フローをより細かく制御することもできます。
例: たぶん
モナドの一例はMaybe型です。未定義の null 結果は、多くの手続き型言語が対処するための特定のツールを提供していない 1 つの問題点です。未定義の値を処理するには、null オブジェクト パターンを使用するか、各操作で無効な値をテストする必要があります。これによりバグが発生し、エラーを適切に処理する堅牢なソフトウェアを構築することが難しくなります。 型Maybeは、結果の 2 つの状態 ( Just ⌑result⌑、 またはNothing) を明示的に定義することにより、プログラマにこれらの潜在的に未定義の結果に対処することを強制します。たとえば、プログラマは、中間結果を返すか、パーサーが検出した条件を通知するパーサーを構築している可能性があります。この条件もプログラマが処理する必要があります。この上に少し機能的なスパイスを加えるだけで、このMaybe型は完全な機能を備えたモナドに変わります。[b] : 12.3 ページ 148–151
ほとんどの言語では、 Maybe モナドはオプション型とも呼ばれ、値が含まれているかどうかを示す型です。通常、これらは何らかの列挙型として表現されます。このRust の例では、これを と呼び、この型のバリアントはジェネリック型Maybe<T>の値か、空のバリアントのいずれかになります。
TNothing
// <T> はジェネリック型 "T" を表します
enum Maybe < T > { Just ( T ), Nothing , }
Maybe<T>は「ラッピング」型としても理解でき、ここでモナドとの関連が出てきます。何らかの形の型を持つ言語には、モナド関数を互いにMaybe合成したり、値が含まれているかどうかをテストしたりするなど、その使用を支援する関数があります。
Maybe
次のハードコードされた例では、Maybe失敗する可能性のある関数の結果として型が使用されています。この場合、ゼロ除算がある場合、型は何も返しません。
fn division ( x : Decimal , y : Decimal ) -> Maybe < Decimal > { if y == 0 { return Nothing } else { return Just ( x / y ) } } // division(1.0, 4.0) -> Just(0.25) を返します// division(3.0, 0.0) -> Nothing を返します
に値が含まれているかどうかをテストする方法の 1 つは、ステートメントMaybeを使用することですif。
let m_x = division ( 3.14 , 0.0 ); // 上記のdivide関数を参照// if文は、m_xがMaybeのJustバリアントである場合に、m_xからxを抽出します。 if let Just ( x ) = m_x { println! ( "answer: " , x ) } else { println! ( "division failed, division by zero error..." ) }
他の言語ではパターンマッチングがあるかもしれない
let result = division ( 3.0 , 2.0 ); match result { Just ( x ) => println! ( "答え: " , x ), Nothing => println! ( "割り算に失敗しました。次回に計算します。" ), }
モナドは、 を返す関数をMaybe組み合わせて作成できます。具体的な例としては、1 つの関数が複数のパラメータを受け取りMaybe、パラメータのいずれかが である場合にMaybe値が である単一の関数を返すことが考えられます。次に例を示します。
NothingNothing
fn chainable_division ( maybe_x : Maybe < Decimal > , maybe_y : Maybe < Decimal > ) -> Maybe < Decimal > { match ( maybe_x , maybe_y ) { ( Just ( x ), Just ( y )) => { // 両方の入力が Just の場合、ゼロ除算をチェックし、それに応じて除算しますif y == 0 { return Nothing } else { return Just ( x / y ) } }, _ => return Nothing // それ以外の場合は Nothing を返します} } chainable_division ( chainable_division ( Just ( 2.0 ), Just ( 0.0 )), Just ( 1.0 )); // 内部の chainable_division は失敗し、外部の chainable_division は Nothing を返します
式を繰り返す代わりに、 bindJust演算子と呼ばれるものを使うことができます。(「map」、「flatmap」、または「shove」[8] : 2205s とも呼ばれます)。この操作は、モナドとモナドを返す関数を受け取り、渡されたモナドの内部値に対して関数を実行し、関数からモナドを返します。
// ".map" を使用した Rust の例。maybe_x は、それぞれ Maybe<Decimal> と Maybe<String> を返す 2 つの関数に渡されます。//
通常の関数合成と同様に、相互に渡される関数の入力と出力は、ラップされた型と一致する必要があります。(つまり、add_one 関数は Maybe<Decimal> を返し、これを Decimal にアンラップして、decimal_to_string 関数に渡す必要があります)
let maybe_x : Maybe < Decimal > = Just ( 1.0 ) let maybe_result = maybe_x . map ( add_one ). map ( decade_to_string )
Haskellには、関数合成に似たよりエレガントな形式でこのモナド合成を可能にする演算子bindまたは( )があります。[c] :150–151 >>=
halve :: Int -> Maybe Int halve x | even x = Just ( x ` div ` 2 ) | odd x = Nothing -- このコードは x を 2 回半分にします。x が 4 の倍数でない場合は Nothing と評価されます。halve x >>= halve
を利用できる場合>>=、匿名関数chainable_division(つまりラムダ)の助けを借りて、より簡潔に表現できます。以下の式では、2 つのネストされたラムダがそれぞれ、 bind 演算子を使用して渡されたモナド内のラップされた値に対してどのように操作するかに注目してください。 [d] : 93 Maybe
連鎖可能除算( mx 、my ) = mx >>= ( λx -> my >>= ( λy -> Just ( x / y )) )
これまで示してきたのは基本的にモナドですが、より簡潔にするために、次のセクションで定義されるモナドに必要な特性の厳密なリストを以下に示します。
- モナド型
- A型(
Maybe)[b] : 148–151 - ユニット操作
- 型変換器(
Just(x))[d] :93 - バインド操作
- モナド関数のコンビネータ(
>>=または.flatMap())[c] :150–151
これらはモナドを形成するために必要な3つの要素です。他のモナドは異なる論理プロセスを具体化する場合があり、いくつかは追加のプロパティを持つ場合がありますが、それらはすべてこれら3つの同様のコンポーネントを持ちます。[1] [9]
意味
上記の例で使用されている関数型プログラミングにおけるモナドのより一般的な定義は、実際にはカテゴリ理論の標準的な定義ではなく、 Kleisli の 3 つの要素⟨T, η, μ⟩ に基づいています。ただし、この 2 つの構成は数学的に同等であることが判明しているため、どちらの定義でも有効なモナドが生成されます。適切に定義された基本型TとUが与えられた場合、モナドは次の 3 つの部分で構成されます。
- モナド型MTを構築する型コンストラクタ M [e]
- 型コンバーターは、unitまたはreturnとも呼ばれ、モナドにオブジェクトxを埋め込みます。
unit : T → M T[女性] - コンビネータは、通常bind (変数をバインドする場合) と呼ばれ、インフィックス演算子または flatMap
>>=と呼ばれるメソッドで表され、モナド変数をアンラップし、それをモナド関数/式に挿入して、新しいモナド値を生成します。(>>=) : (M T, T → M U) → M U[g]mx : M Tでありf : T → M U、ならば、(mx >>= f) : M U
ただし、モナドとして完全に適格となるためには、これら 3 つの部分がいくつかの法則も遵守する必要があります。
- unit はbindの左単位元です:
unit(x) >>= f↔f(x) - unit はbindの右恒等式でもある:
ma >>= unit↔ma - bind は本質的に結合的です: [h]
ma >>= λx → (f(x) >>= g)↔(ma >>= f) >>= g[1]
代数的に言えば、これは任意のモナドがカテゴリ(クライスリカテゴリと呼ばれる)と関数のカテゴリ(値から計算まで)のモノイドの両方を生成し、モナド合成がモノイド[8] :2450 の二項演算子として、単位がモノイドの単位元として存在することを意味します。
使用法
モナドパターンの価値は、単にコードを凝縮し、数学的推論へのリンクを提供するだけにとどまりません。開発者が使用する言語やデフォルトのプログラミングパラダイムが何であれ、モナドパターンに従うことで、純粋関数型プログラミングの利点の多くが得られます。特定の種類の計算を具体化することで、モナドはその計算パターンの面倒な詳細をカプセル化するだけでなく、宣言的な方法でそれを実行し、コードの明瞭性を向上させます。モナド値は計算された値だけでなく計算された効果も明示的に表すため、モナド式は参照透過的な位置でその値に置き換えることができ、純粋式の場合と同様、書き換えに基づく多くのテクニックや最適化が可能になります。[4]
通常、プログラマーはbindを使用してモナド関数をシーケンスに連結します。そのため、モナドを「プログラム可能なセミコロン」と呼ぶ人もいます。これは、多くの命令型言語がステートメントを区切るためにセミコロンを使用する方法を示しています。[1] [5] ただし、モナドは実際には計算を順序付けません。モナドを中心的な機能として使用する言語でも、より単純な関数構成によりプログラム内のステップを整理できます。モナドの一般的な有用性は、むしろプログラムの構造を簡素化し、抽象化を通じて関心の分離を改善することにあります。 [4] [11]
モナド構造は、デコレータパターンのユニークな数学的かつコンパイル時のバリエーションとも見ることができます。一部のモナドは関数がアクセスできない追加データを渡すことができ、さらに一部のモナドは、たとえば特定の条件下でのみ関数を呼び出すなど、実行をより細かく制御できます。アプリケーションプログラマーは、定型コードを事前に開発されたモジュールにオフロードしながらドメインロジックを実装できるため、モナドはアスペクト指向プログラミングのツールと見なすこともできます。[12]
モナドのもう 1 つの注目すべき用途は、純粋に機能的なコード内で、入出力や可変状態などの副作用を分離することです。純粋に機能的な言語でも、関数合成と継続渡しスタイル(CPS)の複雑な組み合わせにより、モナドなしでこれらの「不純な」計算を実装できます。 [2] ただし、モナドを使用すると、基本的に CPS コード内の各繰り返しパターンを個別のモナドにまとめることにより、この足場の多くを抽象化できます。[4]
言語がデフォルトでモナドをサポートしていない場合でも、パターンを実装することは可能であり、多くの場合、それほど困難ではありません。圏論からプログラミング用語に翻訳すると、モナド構造は一般的な概念であり、境界付き多態性に相当する機能をサポートする言語であれば直接定義できます。基礎となる型に取り組みながら操作の詳細について不可知のままでいられる概念の能力は強力ですが、モナドのユニークな機能と厳格な動作は、他の概念とは一線を画しています。[13]
アプリケーション
特定のモナドに関する議論は、通常、特定の計算形式を表す特定のモナドに関する狭い実装問題の解決に焦点が当てられます。ただし、状況によっては、アプリケーションはコア ロジック内で適切なモナドを使用することで、高レベルの目標を達成できる場合もあります。
ここでは、モナドを設計の中心に据えたアプリケーションをいくつか紹介します。
- Parsecパーサライブラリはモナドを使用してより単純な解析ルールをより複雑なものに結合するもので、特に小規模なドメイン固有言語に役立ちます。[14]
- xmonadはジッパーデータ構造を中心としたタイリングウィンドウマネージャであり、それ自体は限定された継続の特定のケースとしてモナド的に扱うことができる。[15]
- MicrosoftのLINQは、クエリをモナド的に構成するためのコア演算子を含む関数型プログラミングの概念に大きく影響を受けた.NET Framework用のクエリ言語を提供します。[16]
- ZipperFSは、主にジッパー構造を使用して機能を実装する、シンプルで実験的なファイルシステムです。 [17]
- リアクティブ拡張フレームワークは本質的には、オブザーバーパターンを実現するデータストリームへの(コ)モナドインターフェースを提供します。[18]
歴史
プログラミングにおける「モナド」という用語は、純粋に関数的な傾向のあるAPLおよびJプログラミング言語に由来しています。ただし、これらの言語では、「モナド」は1つのパラメータを取る関数の省略形にすぎません(2つのパラメータを持つ関数は「ダイアド」など)。[19]
数学者ロジャー・ゴドマンは1950年代後半に初めてモナドの概念を定式化した(「標準構成」と名付けた)が、その後主流となった「モナド」という用語は圏論者サンダース・マクレーンによって普及した。[要出典]ただし、 bindを使用して上で定義された形式は、もともと1965年に数学者ハインリッヒ・クライスリによって、任意のモナドが2つの(共変)関数間の付加として特徴付けられることを証明するために記述された。[20]
1980年代から、モナドパターンの漠然とした概念がコンピュータサイエンスのコミュニティで浮上し始めた。プログラミング言語研究者のフィリップ・ワドラーによると、コンピュータ科学者のジョン・C・レイノルズは、継続渡しスタイルの価値、形式意味論の豊富な情報源としての圏論、値と計算の型の区別について議論した際、1970年代から1980年代初頭にモナドパターンのいくつかの側面を予見していたという。[4] 1990年まで活発に設計されていた 研究言語Opalも、I/Oをモナド型に基づいて効果的に構築したが、当時はその関連性は認識されていなかった。[21]
コンピュータ科学者のエウジェニオ・モッジは、1989年の会議論文で初めて圏論のモナドを関数型プログラミングに明示的に関連付けました。[22]続いて1991年にさらに洗練された論文を雑誌に投稿しました。それ以前の研究では、数人のコンピュータ科学者が圏論を使用してラムダ計算の意味論を提供していました。モッジの重要な洞察は、現実世界のプログラムは値から他の値への関数ではなく、それらの値に基づいて計算を形成する変換であるというものでした。圏論の用語で形式化すると、モナドがこれらの計算を表す構造であるという結論につながります。[3]
このアイデアを広め、発展させた人物は他にも何人かおり、その中にはHaskellの仕様策定に関わったフィリップ・ワドラーやサイモン・ペイトン・ジョーンズも含まれる。特にHaskellは、より柔軟なモナドインターフェースに切り替えるまで、 I/Oと遅延評価を両立させるために、バージョン1.2までは問題のある「遅延ストリーム」モデルを使用していた。 [23] Haskellコミュニティは関数型プログラミングの多くの問題にモナドを適用し、2010年代にはHaskellに取り組む研究者らは、モナドがアプリカティブ・ファンクタであること、[24] [i]、そしてモナドと矢印は両方ともモノイドであることにようやく気付いた。[26]
当初、モナドを使ったプログラミングは Haskell とその派生言語に大きく限定されていましたが、関数型プログラミングが他のパラダイムに影響を与えるにつれて、多くの言語がモナド パターンを (名前ではなく精神で) 取り入れるようになりました。現在、定式化はScheme、Perl、Python、Racket、Clojure、Scala、F#に存在し、新しいML標準にも検討されています。[要出典]
分析
モナド パターンの利点の 1 つは、計算の構成に数学的な精度をもたらすことです。モナドの法則を使用してインスタンスの有効性をチェックできるだけでなく、サブタイプ化を通じて関連する構造 (関数など) の機能を使用することもできます。
モナド法則の検証
例に戻るとMaybe、そのコンポーネントはモナドを構成すると宣言されていますが、それがモナドの法則を満たしているという証明は示されていません。
これは、 の詳細をMaybe一般法則の片側に挿入し、等式の連鎖を代数的に構築して反対側に到達することで修正できます。
法則 1: eta(a) >>= f(x) ≡ (Just a) >>= f(x) ≡ f(a)
法則2: ma >>= eta(x) ≡ ma
もしmaが(ただの a)ならば
eta(a) ≠ ただの a
そう でなければ
何もない ≠ 何もない
終了の場合
法則 3: ( ma >>= f(x) ) >>= g(y) ⇔ ma >>= ( f(x) >>= g(y) )
もし(ma >>= f(x))が(Just b)ならば、 もしmaが(Just a)ならば
g(ma >>= f(x)) (f(x) >>= g(y))
他 に
何もない 何もない
終了の場合 終了の場合
≡ maが(ただa)でf(a)が(ただb)ならば
(g ∘ f) ア
そうでない場合、 ma は(Just a)であり、 f(a) はNothing である。
何もない
それ以外
何もない
終了の場合
関数からの導出
コンピュータサイエンスでは稀ですが、モナドを2つの追加の自然変換を持つ関数として定義するカテゴリ理論を直接使用することができます。[j] まず、構造が関数として適格となるには、 mapという名前の高階関数(または「関数型」)が必要です。
map : (a → b) → (ma → mb)ただし、これは必ずしも大きな問題ではありません。特に、モナドが既存のファンクタから派生している場合は、モナドは自動的にmap を継承します。(歴史的な理由により、これはHaskell ではmap代わりに と呼ばれますfmap。)
モナドの最初の変換は、実際にはKleisli トリプルのunitと同じですが、構造の階層を厳密にたどると、 unit は、モナドと基本関数の中間構造であるapplicative functor を特徴付けることがわかります。applicative のコンテキストでは、unit は純粋と呼ばれることもありますが、それでも同じ関数です。この構成で異なるのは、unit が満たさなければならない法則です。bindが定義されていないため、制約は代わりにmapで与えられます。
(unit ∘ φ) x ↔ ((map φ) ∘ unit) x ↔ x[27]アプリカティブ・ファンクタからモナドへの最終的な飛躍は、2 番目の変換であるjoin関数 (カテゴリ理論では、これは通常μと呼ばれる自然な変換です) によって実現され、モナドのネストされたアプリケーションを「平坦化」します。
join(mma) : M (M T) → M T特性関数として、join はモナド法則の 3 つのバリエーションも満たす必要があります。
(join ∘ (map join)) mmma ↔ (join ∘ join) mmma ↔ ma(join ∘ (map unit)) ma ↔ (join ∘ unit) ma ↔ ma(join ∘ (map map φ)) mma ↔ ((map φ) ∘ join) mma ↔ mb開発者が直接モナドを定義するか、Kleisli トリプルを定義するかに関係なく、基礎となる構造は同じであり、フォームは互いに簡単に導出できます。
(map φ) ma ↔ ma >>= (unit ∘ φ)join(mma) ↔ mma >>= idma >>= f ↔ (join ∘ (map f)) ma[28]別の例: リスト
リストモナドは、より単純なファンクタからモナドを派生させることがいかに便利であるかを自然に示しています。多くの言語では、リスト構造はいくつかの基本的な機能とともに事前に定義されているため、List型コンストラクタと追加演算子 (++インフィックス表記では で表されます) は、ここではすでに与えられていると想定されます。
リストにプレーンな値を埋め込むことも、ほとんどの言語では簡単です。
単位(x) = [x]
ここから、リストの内包表記を使用して関数を反復的に適用することは、 bindにとって簡単な選択肢のように思え、リストを完全なモナドに変換します。このアプローチの難しさは、bind がモナド関数を期待し、この場合はリスト自体を出力することです。適用される関数が増えるにつれて、ネストされたリストのレイヤーが蓄積され、基本的な内包表記以上のものが必要になります。
ただし、リスト全体、つまりmap に任意の単純な関数を適用する手順は簡単です。
(マップ φ) xlist = [ φ(x1), φ(x2), ..., φ(xn) ]
現在、これら 2 つの手順は、すでにListアプリカティブ ファンクタに昇格しています。モナドとして完全に適格になるには、繰り返し構造を平坦化するための結合の正しい概念のみが必要ですが、リストの場合、それは単に外側のリストをアンラップして、値を含む内側のリストを追加することを意味します。
join(xlistlist) = join([xlist1, xlist2, ..., xlistn])
= xlist1 ++ xlist2 ++ ... ++ xlistn
結果として得られるモナドはリストであるだけでなく、関数が適用されると自動的にサイズが変更され、圧縮されます。bind
は数式だけで導出することもでき、その後、Listモナド関数のパイプラインに値を供給するために使用できます。

Listは複素根のような多値関数の使用を大幅に簡素化することができます。[29](xlist >>= f) = 結合 ∘ (map f) xlist
このモナドリストの応用例の 1 つは、非決定的計算を表すことです。
Listアルゴリズムのすべての実行パスの結果を保持し、各ステップで自身を凝縮して、どのパスがどの結果につながったかを「忘れる」ことができます (決定的、網羅的なアルゴリズムとの重要な違いとなる場合があります)。[要出典]
もう 1 つの利点は、チェックをモナドに埋め込むことができることです。特定のパスは、パイプライン内の関数を書き直すことなく、最初の障害発生時点で透過的に削除できます。[28]
が輝く2番目の状況は、多価関数Listの作成です。たとえば、ある数のn次の複素根はn個の異なる複素数を生成しますが、その結果から別のm次の根をとった場合、最終的なm•nの値はm•n次の根の出力と同一になります。
はこの問題を完全に自動化し、各ステップの結果を平坦で数学的に正しいリストに凝縮します。[29]List
テクニック
モナドは、プログラム ロジックを整理するだけにとどまらない興味深いテクニックの機会を提供します。モナドは、高レベルかつ数学的な性質によって大幅な抽象化を可能にすると同時に、有用な構文機能の基礎を築くことができます。
糖衣構文ド記法
bindをオープンに使用することは多くの場合理にかなっていますが、多くのプログラマは命令文を模倣した構文(Haskellではdo記法、 OCamlでは実行記法、F#では計算式、[30] 、 Scalaではfor内包表記と呼ばれる)を好みます。これはモナドパイプラインをコードブロックとして偽装する単なる糖衣構文であり、コンパイラはこれらの式を基礎となる関数型コードに静かに変換します。
add関数をHaskell に翻訳するとMaybe、この機能が実際に動作する様子を見ることができます。Haskell の非モナド バージョンはadd次のようになります。
mx my = case mx of Nothing -> Nothing Just x -> case my of Nothing -> Nothing Just y -> Just ( x + y )を追加します。
モナド Haskell では、 はunitreturnの標準名であり、ラムダ式は明示的に処理する必要がありますが、これらの技術的な詳細があっても、モナドはより明確な定義になります。
Maybe
mx my = mx >>= ( \ x -> my >>= ( \ y -> return ( x + y )))を追加します。
しかし、do 記法を使用すると、これをさらに非常に直感的なシーケンスに凝縮できます。
mx my = do x <- mx y <- myを追加して( x + y )を返します。
2 番目の例は、まったく異なる言語である F# でどのように使用できるかを示しています。計算式を使用すると、未定義のオペランドまたはMaybeゼロ除算を返す「安全な除算」関数は次のように記述できます。
None
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 . Delay ( fun () ->
maybe . Bind ( readNum () , fun x ->
maybe . Bind ( readNum () , fun y ->
if ( y = 0 ) then None else maybe . Return ( x / y ))))
最後の例として、一般的なモナド法則自体も do 記法で表現できます。
do { x <- return v ; f x } == do { f v } do { x <- m ; return x } == do { m } do { y <- do { x <- m ; f x }; g y } == do { x < - m ; y <- f x ; g y }
一般的なインターフェース
すべてのモナドは、モナドの法則を満たす特定の実装を必要としますが、他の構造との関係や言語内の標準的なイディオムなどの他の側面は、すべてのモナドで共有されます。その結果、言語またはライブラリは、関数プロトタイプMonad、サブタイプ関係、およびその他の一般的な事実を備えた一般的なインターフェイスを提供する場合があります。開発の有利な開始を提供し、新しいモナドがスーパータイプ(ファンクタなど)から機能を継承することを保証するだけでなく、インターフェイスに対してモナドの設計をチェックすることで、品質管理の別のレイヤーが追加されます。[引用が必要]
オペレーター
モナドコードは、演算子をうまく使うことで、さらに単純化できることが多いです。map関数は、アドホックなモナド関数だけでなく、さまざまな関数で使えるので、特に便利です。モナド関数が定義済みの演算子と同じように機能する限り、map を使って、より単純な演算子をモナド関数に瞬時に「持ち上げる」ことができます。[k]addこの手法を使うと、例の
の定義はMaybe次のように要約できます。
追加(mx,my) = マップ(+)
addこのプロセスは、だけでなくインターフェースMaybe全体に対して を定義することでさらに一歩進めることができますMonad。こうすることで、構造インターフェースに一致し、独自のマップを実装する新しいモナドは、のリフトされたバージョンも直ちに継承するようになりますadd。関数に必要な唯一の変更は、型シグネチャを一般化することです。
追加:(モナド数、モナド数)→モナド数[31]
分析に役立つもう 1 つのモナド演算子は、モナド合成 (>=>ここではインフィックスとして表されます) です。これにより、モナド関数をより数学的なスタイルで連鎖できるようになります。
(f >=> g)(x) = f(x) >>= g
この演算子を使用すると、モナドの法則を関数のみで記述することができ、結合性と恒等式の存在との対応が強調されます。
(単位 >=> g) ↔ g (f >=> 単位) ↔ f (f >=> g) >=> h ↔ f >=> (g >=> h) [1]
上記は、Haskell の「do」ブロックの意味を示しています。
する _p <- f(x) _q <- g(_p) h(_q) ↔ ( f >=> g >=> h )(x)
その他の例
アイデンティティモナド
最も単純なモナドは、モナドの法則を満たすために単純な値と関数に注釈を付けるだけの Identity モナドです。
新しい型ID T = T 単位(x) = x (x >>= f) = f(x)
Identityしかし、再帰モナド変換の基本ケースを提供するなど、実際には有効な用途があります。また、命令型ブロック内で基本的な変数割り当てを実行するためにも使用できます。[l] [引用が必要]
コレクション
適切な追加を持つコレクションはどれもすでに自由モノイドですが、明確に定義された結合を持ち、モナドとして適格なコレクションはListそれだけではありません。追加に特別なプロパティを課すだけで、これらの他のモナドコレクションに変化することさえできます。[m] [n]List
IO モナド (Haskell)
すでに述べたように、純粋なコードには管理されていない副作用があってはなりませんが、プログラムが明示的に副作用を記述して管理することを妨げるものではありません。この考え方は Haskell のIO モナドの中心的な考え方です。ここで、 型のオブジェクトはIO a、世界で実行されるアクションを記述するものと見なすことができ、オプションで 型 の世界に関する情報を提供します。a世界に関する情報を提供しないアクションは 型を持ちIO ()、ダミー値 を「提供」します()。プログラマがIO関数に値をバインドすると、関数は、前のアクションによって提供された世界に関する情報 (ユーザー、ファイルなどからの入力) に基づいて、次に実行されるアクションを計算します。[23]最も重要なのは、IO モナドの値は別の IO モナドを計算する関数にのみバインドできるため、bind 関数はアクションのシーケンスの規律を課し、アクションの結果は、次に実行するアクションを計算する関数にのみ提供できるということです。これは、実行する必要がないアクションは決して実行されず、実行する必要があるアクションには明確に定義されたシーケンスがあり、(IO) アクションが参照透過的ではないという問題が解決されることを意味します。
たとえば、Haskell には、ファイルが存在するかどうかを確認する関数やファイルを削除する関数など、より広範なファイル システムを操作する関数がいくつかあります。それらの 2 つの型シグネチャは次のとおりです。
doesFileExist :: FilePath -> IOブール値removeFile :: FilePath -> IO ()
最初の関数は、指定されたファイルが実際に存在するかどうかに関心があり、その結果、モナド内でブール値IOを出力します。一方、2 番目の関数は、ファイル システムに対する操作のみに関心があるため、IO出力されるコンテナーは空になります。
IOただし、ファイル I/O だけに限定されるわけではなく、ユーザー I/O も許可され、命令型の構文シュガーと組み合わせることで、典型的な「Hello, World!」プログラムを模倣できます。
main :: IO () main = do putStrLn "Hello, world!" putStrLn "あなたのお名前は何ですか、user?" name <- getLine putStrLn ( "初めまして、" ++ name ++ "!" )
これを desugar すると、次のモナド パイプラインに変換されます ( Haskell では、モナド効果のみが重要で、基礎となる結果を破棄できる場合の
bind>>の単なるバリエーションです)。
main :: IO () main = putStrLn "Hello, world!" >> putStrLn "あなたのお名前は何ですか、user?" >> getLine >>= ( \ name -> putStrLn ( "初めまして、" ++ name ++ "!" ))
ライターモナド (JavaScript)
もう一つの一般的な状況は、ログ ファイルを保存したり、プログラムの進行状況を報告することです。プログラマーは、後でプロファイリングやデバッグを行うために、さらに具体的で技術的なデータをログに記録したい場合があります。Writerモナドは、ステップごとに蓄積される補助出力を生成することで、これらのタスクを処理できます 。
モナド パターンが主に関数型言語に限定されないことを示すために、この例ではJavaScriptWriterでモナドを実装します。まず、配列 (ネストされた末尾を持つ) により、型をリンク リストとして構築できます。基になる出力値は配列の位置 0 に存在し、位置 1 には補助ノートのチェーンが暗黙的に保持されます。
Writer
constライター=値=> [値, []];
単位の定義も非常に簡単です。
const単位=値=> [値, []];
デバッグ ノート付きのオブジェクトを
出力する単純な関数を定義するには、ユニットのみが必要です。Writer
const squared = x => [ x * x , [ ` ${ x }が二乗されました。` ]]; const halved = x => [ x / 2 , [ ` ${ x }が半分になりました。` ]];
真のモナドには依然としてbind が必要ですが、 の場合Writer、これは単に関数の出力をモナドのリンクリストに連結することになります。
const bind = ( writer , transform ) => { const [ value , log ] = writer ; const [ result , updates ] = transform ( value ); return [ result , log . concat ( updates )]; };
サンプル関数はbind を使用して連結できるようになりましたが、モナド合成のバージョン (pipelogここで呼び出されます) を定義すると、これらの関数をさらに簡潔に適用できます。
const pipelog = ( writer , ... transforms ) => transforms .reduce ( bind , writer ) ;
最終結果は、計算をステップ実行することと、後で監査するためにログに記録することの間の懸念が明確に分離されます。
pipelog ( unit ( 4 ), squared , halved ); // 結果の writer object = [8, ['4 は 2 乗されました。', '16 は 2 分の 1 になりました。']]
環境モナド
環境モナド (リーダー モナドや関数モナドとも呼ばれる) は、計算が共有環境の値に依存することを可能にします。モナドの型コンストラクタは、型T を型E → Tの関数にマッピングします。ここで、Eは共有環境の型です。モナド関数は次のとおりです。
次のモナド演算が便利です。
ask操作は現在のコンテキストを取得するために使用され、local は変更されたサブコンテキストで計算を実行します。状態モナドと同様に、環境モナドの計算は、環境値を指定してそれをモナドのインスタンスに適用するだけで呼び出すことができます。
正式には、環境モナドの値は追加の匿名引数を持つ関数と同等です。returnとbind は、それぞれSKI コンビネータ計算のKコンビネータとSコンビネータと同等です。
状態モナド
状態モナドを使用すると、プログラマは任意の型の状態情報を計算に添付できます。任意の値型が与えられた場合、状態モナドの対応する型は、状態を受け入れ、新しい状態 (型s) と戻り値 (型t) を出力する関数です。これは環境モナドに似ていますが、新しい状態も返すため、変更可能な環境をモデル化できます。
型状態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 ::状態s a -> s -> ( a 、s ) runState t s = t s
状態モナドの do ブロックは、状態データを調べて更新できる一連の操作です。
非公式には、状態型Sの状態モナドは、戻り値Tの型を型の関数にマッピングします。ここで、Sは基礎となる状態です。戻り関数とバインド関数は次のとおりです。
- 。
カテゴリー理論の観点から見ると、状態モナドは、定義により任意のデカルト閉カテゴリーに存在する積関数と指数関数の間の随伴関係から導出されます。
継続モナド
戻り型Rを持つ継続モナド[o]は、型Tを型 の関数にマッピングします。これは継続渡しスタイルをモデル化するために使用されます。戻り関数とバインド関数は次のとおりです。
call -with-current-continuation関数は次のように定義されます。
プログラムログ
次のコードは疑似コードです。foo2つの関数とがありbar、その型が
foo : int -> int bar : int -> int
つまり、両方の関数は整数を受け取り、別の整数を返します。次に、次のように関数を連続して適用できます。
foo (バーx )
ここで、結果は に適用された の結果、foo結果はbarに適用されたの結果ですx。
しかし、プログラムをデバッグしていて、fooとにログメッセージを追加したいとしますbar。その場合、型を次のように変更します。
foo : int -> int *文字列bar : int -> int *文字列
両方の関数は、アプリケーションの結果を整数として、適用された関数と以前に適用されたすべての関数に関する情報を含むログ メッセージを文字列として含むタプルを返します。
残念ながら、これはと を合成 できなくなったことを意味します。それらの入力型が出力型 と互換性がないためです。また、各関数の型を に変更することで再び合成可能性を得ることができますが、これにはタプルから整数を抽出するための定型コードを各関数に追加する必要があり、そのような関数の数が増えるにつれて面倒になります。
foobarintint * stringint * string -> int * string
代わりに、この定型文を抽象化するヘルパー関数を定義しましょう。
バインド: int *文字列-> ( int -> int *文字列) -> int *文字列
bindは整数と文字列のタプルを受け取り、次にfoo整数を整数と文字列のタプルにマップする関数 ( など) を受け取ります。その出力は整数と文字列のタプルであり、これは入力関数を入力整数と文字列のタプル内の整数に適用した結果です。このように、 でタプルから整数を 1 回抽出するための定型コードを記述するだけで済みますbind。
これで、ある程度の構成可能性が回復しました。例:
バインド(バインド( x 、s )バー) foo
は(x,s)整数と文字列のタプルです。[p]
利点をさらに明確にするために、 のエイリアスとして中置演算子を定義しましょうbind。
( >>= ) : int *文字列-> ( int -> int *文字列) -> int *文字列
つまり、t >>= fと同じですbind t f。
上記の例は次のようになります。
(( x , s ) >>= bar ) >>= foo
(x, "")最後に、空のログ メッセージを作成するたびに を書き込まないようにするための新しい関数を定義します。ここでは""、 は空の文字列です。
戻り値: int -> int *文字列
これは、x上で説明したタプルをラップします。
結果は、メッセージをログに記録するためのパイプラインです。
((戻り値x ) >>= bar ) >>= foo
barこれにより、とfooの効果をより簡単に記録できるようになりますx。
int * stringは擬似コード化されたモナド値を表します。[p] bindおよびは、return同じ名前の対応する関数に類似しています。実際、、、int * stringおよびbindはreturnモナドを形成します。
加法モナド
加法モナドは、追加の閉じた結合二項演算子mplusと、mplusの単位元mzeroを備えたモナドです。モナドは加法的であると考えることができ、mzeroとOR演算子のバリエーションがmplusとして機能します。
も加法モナドであり、空のリストがmzeroとして機能し、連結演算子がmplusとして機能します。
MaybeNothingList[]++
直感的には、mzero は基になる型からの値を持たないモナド ラッパーを表しますが、bindの吸収体として動作し、モナド関数にバインドされるたびにmzeroを返すため、「1」ではなく「ゼロ」と見なされます。このプロパティは双方向であり、bind は、任意の値がモナドゼロ関数にバインドされている場合にもmzero を返します。
圏論的な用語では、加法モナドはbindによってモナド関数上のモノイドとして一度は適格となり(すべてのモナドがそうであるように)、さらにmplusによってモナド値上のモノイドとして再度適格となる。[32] [q]
自由モナド
場合によっては、モナドの一般的な概要が役立つこともありますが、単純なパターンでは特定のモナドを推奨することはできません。ここで、自由モナドが登場します。自由モナドは、モナドのカテゴリ内の自由オブジェクトとして、モナドの法則自体以外の特定の制約なしにモナド構造を表すことができます。自由モノイドが評価なしで要素を連結するのと同じように、自由モナドは、型システムを満たすためにマーカーを使用して計算を連鎖させることができますが、それ以外には、より深いセマンティクスを課しません。
たとえば、JustとNothingマーカーを完全に処理すると、Maybeモナドは実際には自由モナドになります。List一方、 モナドは、リストに関する追加の特定の事実 ( appendなど) を定義に取り入れているため、自由モナドではありません。最後の例は、抽象的な自由モナドです。
データフリーf a =ピュアa |フリー( f (フリーf a ))
ユニット:: a - >自由fユニットx =純粋x
bind :: Functor f => Free f a -> ( a -> Free f b ) -> Free f b bind ( Pure x ) f = f x bind ( Free x ) f = Free ( fmap ( \ y -> bind y f ) x )
ただし、自由モナドは、この例のようなリンクリストに限定されず、ツリーなどの他の構造を中心に構築できます。
自由モナドを意図的に使用することは、最初は非実用的に思えるかもしれませんが、その形式的な性質は、構文上の問題に特に適しています。自由モナドは、意味論を後回しにして構文と型を追跡するために使用することができ、その結果、パーサーやインタープリターで使用されています。 [33]他の人は、言語内で反復子を 提供するなど、より動的で操作的な問題にも自由モナドを適用しています。 [34]
コモナド
追加のプロパティを持つモナドを生成することに加えて、任意のモナドに対して、コモナドを定義することもできます。概念的には、モナドが基礎となる値から構築された計算を表す場合、コモナドは値への還元として見ることができます。ある意味では、モナド コードは完全に「アンパック」することはできません。値がモナド内にラップされると、副作用とともにそこに隔離されたままになります (純粋に関数型のプログラミングでは良いことです)。ただし、場合によっては、問題はコンテキスト データの消費に関するものであり、コモナドはそれを明示的にモデル化できます。
技術的には、コモナドはモナドのカテゴリカルデュアルであり、大まかに言えば、型シグネチャの方向が逆になっているだけで、必要なコンポーネントは同じであることを意味します。bind中心のモナドの定義から始めると、コモナドは次の要素で構成されます。
- 高階型WTを表す型コンストラクタW
- unitのデュアル(ここではcounitと呼ばれます) は、コモナドから基礎となる値を抽出します。
係数(wa) : WT → T
- bindの逆関数( とも表記
=>>) は、 を一連の還元関数に拡張します。
(wa =>> f) : (WU, WU → T) → WT [r]
extendとcounit はモナド法則の双対性も満たす必要があります。
カウント単位 ∘ ( (wa =>> f) → wb ) ↔ f(wa) → b wa =>> 町 ↔ wa wa ( (=>> f(wx = wa)) → wb (=>> g(wy = wb)) → wc ) ↔ ( wa (=>> f(wx = wa)) → wb ) (=>> g(wy = wb)) → wc
モナドと同様に、コモナドもjoinの双対を使って関数から導出できます。
- duplicate は、すでにコモナディックな値を受け取り、それを別のコモナディック構造のレイヤーでラップします。
複製(wa) : WT → W (WT)
ただし、 extendのような操作は逆になりますが、コモナドはそれが作用する関数を逆に戻さないため、結果的に、コモナドは依然としてmapを持つ関数であり、共関数ではありません。 duplicate、counit、およびmap を持つ代替定義も、独自のコモナド法則を尊重する必要があります。
((マップ重複) ∘ 重複) wa ↔ (重複 ∘ 重複) wa ↔ wwwa ((マップ個数) ∘ 重複) わ ↔ (個数 ∘ 重複) わ ↔ わ ((マップ マップ φ) ∘ 重複) wa ↔ (重複 ∘ (マップ φ)) wa ↔ wwb
モナドと同様に、2 つの形式は自動的に変換できます。
(マップ φ) ワ ↔ ワ =>> (φ ∘ カウント) wx wa ↔ wa =>> wx を重複する
wa =>> f(wx) ↔ ((map f) ∘ duplicate) wa
簡単な例としては、入力値と共有環境データに基づいて値を出力するためのProduct コモナドProductがあります。実際、コモナドはモナドのデュアルでありWriter、実質的にはモナドと同じですReader(両方とも後述)。コモナド
ProductとモナドReaderの違いは、受け入れる関数シグネチャと、値をラップまたはアンラップしてそれらの関数を補完する方法だけです。
それほど重要ではない例としては、ストリームコモナドがあります。これは、データストリームを表現し、 extendを使用して入力信号にフィルターを添付するために使用できます。実際、モナドほど人気はありませんが、研究者はコモナドがストリーム処理やデータフロープログラミングのモデリングに特に役立つことを発見しました。[35] [36]
しかし、その厳密な定義のため、単純にモナドとコモナドの間でオブジェクトを移動させることはできません。さらに高い抽象度として、矢印は両方の構造を包含することができますが、モナドとコモナドのコードを組み合わせるより細かい方法を見つけることは、活発な研究分野です。[37] [38]
参照
モデル化計算の代替手段:
関連するデザインコンセプト:
- アスペクト指向プログラミングでは、補助的な簿記コードを分離してモジュール性とシンプルさを向上させることに重点を置いています。
- 制御の反転とは、包括的なフレームワークから特定の機能を呼び出すという抽象的な原則である。
- 型クラスはHaskellでモナドやその他の構造を実装するために使用される特定の言語機能です。
- デコレータパターンは、オブジェクト指向プログラミングで同様の利点を実現するための、より具体的でアドホックな方法です。
モナドの一般化:
- アプリカティブファンクタは、単位とそれをマップに関連付ける法則のみを保持することでモナドから一般化します。
- 矢印は追加の構造を使用して、単純な関数とモナドを単一のインターフェースの下にまとめます。
- モナド変換子は、異なるモナドに作用してそれらをモジュール的に結合します。
注記
- ^複数の 自由変数に対する関数はプログラミングでは一般的であるため、この記事で説明するモナドは、技術的には圏論者が強いモナドと呼ぶものである。[3]
- ^ ab Maybeの具体的な動機については、(Hutton 2016)を参照してください。[7]
- ^ ab Huttonは、失敗する可能性のある
bind型aと、失敗する可能性のあるマッピングa → bが与えられた場合に、失敗する可能性のある結果bを生成するaを抽象化します。(Hutton、2016)[7] - ^ ab (Hutton 2016)は、Justは成功を意味し、Nothingは失敗を意味する可能性があると指摘している。[7]
- ^ 意味的には、M は自明ではなく、すべての適切に型付けされた値のカテゴリ上の自己関数を表します。
- ^ プログラミング用語では(パラメトリック多相)関数ですが、単位(圏理論ではηと呼ばれることが多い)は数学的には関数間をマッピングする自然変換です。
- 一方、bindは圏論における自然な変換ではなく、値から計算へのマッピングを計算間の射に持ち上げる拡張です。
- ^ 厳密に言えば、bindは数学ではなくラムダ計算内のアプリケーションに対応するため、すべてのコンテキストで形式的に結合的であるとは限りません。厳密なラムダ計算では、 bindを評価するには、最初に右側の項(2つのモナド値をバインドする場合)またはbind自体(2つのモナド関数間)を匿名関数でラップして、左側からの入力を受け入れる必要がある場合があります。[10]
- ^ GHCバージョン7.10.1以降、HaskellはHaskellの2014年のApplicative Monad提案(AMP)の施行を開始し、モナドを使用する既存のモジュールに7行のコードを挿入することを要求するようになりました。[25]
- ^ これらの自然変換は通常、射 η、μ として表されます。つまり、η、μ はそれぞれ単位、結合を表します。
- ^ Haskell などの一部の言語では、他のコンテキストでのmapの仮名として が提供されており、パラメータ数が異なる複数のバージョンも用意されていますが、この詳細はここでは無視します。
lift - ^ 圏論では、モナドは任意の関数とその逆関数の随伴
Identityから生じると見ることもできる。 - ^ 圏論では、これらのコレクションモナドを、自由関数と、集合の圏からモノイドの圏までのさまざまな関数との間の付加物として見なします。
- ^ ここでプログラマーのタスクは、適切なモノイドを構築すること、あるいはライブラリからモノイドを選択することです。
- ^読者はマッキャンのスレッド [6]をたどり、それを以下のタイプと比較したいと思うかもしれない。
- ^ ab この場合、以前は だけだった場所にがを貼り付けています
bind。つまり、プログラマーは、上記の疑似コード § で示されているように、付加物、つまりタプルを構築したことになります。stringinteger(x,s)int * string - ^ 代数的には、2つの(非可換な)モノイドの側面の関係は近半環の関係に似ており、いくつかの加法モナドはそのような関係に該当します。しかし、すべての加法モナドが近半環の分配法則を満たすわけではありません。 [32]
- ^ Haskell では、extend は実際には入力を入れ替えて定義されますが、この記事ではカリー化は使用されていないため、ここではbindの正確なデュアルとして定義されます。
参考文献
- ^ abcdef オサリバン、ブライアン、ゴーゼン、ドン・スチュワート (2009)。「モナド」。リアルワールドハスケル。カリフォルニア州セバストポル:オライリーメディア。第 14 章。ISBN 978-0596514983。
- ^ ab Wadler, Philip (1990 年 6 月). Comprehending Monads . ACM Conference on LISP and Functional Programming. ニース、フランス. CiteSeerX 10.1.1.33.5381 .
- ^ abc Moggi, Eugenio (1991). 「計算とモナドの概念」(PDF) .情報と計算. 93 (1): 55–92. CiteSeerX 10.1.1.158.5275 . doi :10.1016/0890-5401(91)90052-4.
- ^ abcde Wadler, Philip (1992 年 1 月).関数型プログラミングの本質. 19th Annual ACM Symposium on Principles of Programming Languages. アルバカーキ、ニューメキシコ. CiteSeerX 10.1.1.38.9516 .
- ^ ab Hudak, Paul ; Peterson, John; Fasel, Joseph (1999). 「モナドについて」。Haskell 98 入門書、第 9 章。
- ^ ab CA McCann の回答 (2010 年 7 月 23 日 23:39) Haskell Cont モナドはなぜ、どのように動作するのでしょうか?
- ^ abc グラハム・ハットン (2016) Haskell プログラミング第 2 版
- ^ ab Beckerman, Brian (2012年11月21日). 「モナドを恐れるな」YouTube。
- ^ Spivey, Mike (1990). 「例外の機能理論」(PDF) .コンピュータプログラミングの科学. 14 (1): 25–42. doi : 10.1016/0167-6423(90)90056-J .
- ^ 「モナドの法則」HaskellWiki.haskell.org . 2018年10月14日閲覧。
- ^ 「モナドとは何か」 2018年10月7日。
- ^ De Meuter, Wolfgang (1997). AOP の理論的基礎としてのモナド(PDF) . ECOOP におけるアスペクト指向プログラミングに関する国際ワークショップ。ユヴァスキュラ、フィンランド。CiteSeerX 10.1.1.25.8262 。
- ^ 「モナド(メタファーなし)」HaskellWiki。2009年11月1日。 2018年10月24日閲覧。
- ^ O'Sullivan, Bryan; Goerzen, John; Stewart, Don (2009). 「Parsec の使用」. Real World Haskell. セバストポル、カリフォルニア州: O'Reilly Media. 第 16 章. ISBN 978-0596514983。
- ^ Stewart, Don (2007 年 5 月 17 日). 「Roll Your Own Window Manager: Tracking Focus with a Zipper」. Control.Monad.Writer . 2018 年 2 月 20 日時点のオリジナルよりアーカイブ。 2018 年11 月 19 日閲覧。
- ^ Benton, Nick (2015). 「Categorical Monads and Computer Programming」(PDF) .ロンドン数学会Impact150 Stories . 1. 2018年11月19日閲覧。
- ^ Kiselyov, Olag (2007)。「オペレーティングシステムにおける限定継続」。コンテキストのモデリングと使用。コンピュータサイエンスの講義ノート。第 4635 巻。Springer Berlin Heidelberg。291--302 ページ。doi : 10.1007/978-3-540-74255-5_22。ISBN 978-3-540-74255-5。
- ^ マイヤー、エリック(2012年3月27日)。「マウスはデータベースです」。ACM Queue。10 ( 3):20–33。doi :10.1145 / 2168796.2169076。
- ^ アイバーソン、ケネス(1987年9月)。「A PL辞書」。APL Quote Quad。18(1):5–40。doi :10.1145 / 36983.36984。ISSN 1088-6826。S2CID 18301178。2018年11月19 日閲覧。
- ^ Kleisli, Heinrich (1965). 「すべての標準構成は、一対の随伴関数によって誘導される」( PDF)。アメリカ数学会紀要。16 (3): 544–546。doi : 10.1090/S0002-9939-1965-0177024-4 。 2018年11月19日閲覧。
- ^ ピーター・ペッパー編。 (1997年11月)。プログラミング言語 Opal (技術レポート) (改訂第 5 版)。 Fachbereich Informatik、ベルリン工科大学。CiteSeerX 10.1.1.40.2748。
- ^ Moggi, Eugenio (1989 年 6 月). 計算ラムダ計算とモナド(PDF) . 第 4 回コンピュータサイエンスにおける論理に関するシンポジウム. カリフォルニア州パシフィックグローブ. CiteSeerX 10.1.1.26.2787 .
- ^ ab Peyton Jones, Simon L. ; Wadler, Philip ( 1993 年 1 月). 命令型関数型プログラミング(PDF) . 第 20 回 ACM プログラミング言語の原理に関するシンポジウム。サウスカロライナ州チャールストン。CiteSeerX 10.1.1.53.2504。
- ^ ブレント・ヨーギー Typeclassopedia
- ^ Stack Overflow (2017年9月8日) Haskellで新しいモナドを定義してもApplicativeのインスタンスが生成されない
- ^ ブレント・ヨーギー モノイド
- ^ “Applicative functor”. HaskellWiki . Haskell.org. 2018年5月7日. 2018年10月30日時点のオリジナルよりアーカイブ。 2018年11月20日閲覧。
- ^ ab Gibbard, Cale (2011年12月30日). 「Monads as containers」. HaskellWiki . Haskell.org. 2017年12月14日時点のオリジナルよりアーカイブ。 2018年11月20日閲覧。
- ^ ab Piponi, Dan (2006年8月7日). 「あなたはモナドを発明できたかもしれない!(そして、おそらくあなたはすでに発明しているでしょう。)」A Neighborhood of Infinity。 2018年10月24日時点のオリジナルよりアーカイブ。 2018年10月16日閲覧。
- ^ 「F# 計算式の詳細」。2007 年 9 月 21 日。2018 年10 月 9 日に閲覧。
- ^ Giles, Brett (2013年8月12日). 「Lifting」. HaskellWiki . Haskell.org. 2018年1月29日時点のオリジナルよりアーカイブ。 2018年11月25日閲覧。
- ^ ab Rivas, Exequiel; Jaskelioff, Mauro; Schrijvers, Tom (2015 年 7 月). モノイドから近似半環へ: MonadPlus と Alternative の真髄(PDF) .第 17 回国際 ACM 宣言型プログラミングの原理と実践シンポジウム。イタリア、シエナ。CiteSeerX 10.1.1.703.342。
- ^ Swierstra, Wouter (2008). 「Data types à la carte」(PDF) . Functional Pearl. Journal of Functional Programming . 18(4). Cambridge University Press: 423–436. CiteSeerX 10.1.1.101.4131 . doi :10.1017/s0956796808006758(2024年11月1日現在非アクティブ)。ISSN 1469-7653 . S2CID 21038598.
{{cite journal}}: CS1 maint: DOI inactive as of November 2024 (link) - ^ Kiselyov, Oleg (2012 年 5 月). Schrijvers, Tom; Thiemann, Peter (編). Iteratees (PDF) . 関数型および論理プログラミングに関する国際シンポジウム. コンピュータサイエンスの講義ノート. 第 7294 巻. 神戸、日本: Springer-Verlag. pp. 166–181. doi :10.1007/978-3-642-29822-6_15. ISBN 978-3-642-29822-6。
- ^ Uustalu, Tarmo; Vene, Varmo (2005 年 7 月)。 Horváth, Zoltán (編)。 データフロー プログラミングの真髄(PDF)。 第一回サマー スクール、中央ヨーロッパ関数型プログラミング。 コンピュータ サイエンスの講義ノート。 Vol. 4164。 ブダペスト、ハンガリー: Springer-Verlag。 pp. 135–167。CiteSeerX 10.1.1.62.2047。ISBN 978-3-540-46845-5。
- ^ Uustalu, Tarmo; Vene, Varmo (2008 年 6 月). 「Comonadic Notions of Computation」. Electronic Notes in Theoretical Computer Science . 203 (5). Elsevier: 263–284. doi :10.1016/j.entcs.2008.05.029. ISSN 1571-0661.
- ^ Power, John; Watanabe, Hiroshi (2002年5月). 「モナドとコモナドの結合」(PDF) .理論計算機科学. 280 (1–2). Elsevier: 137–162. CiteSeerX 10.1.1.35.4130 . doi :10.1016/s0304-3975(01)00024-x. ISSN 0304-3975.
- ^ Gaboardi, Marco; Katsumata, Shin-ya; Orchard, Dominic; Breuvart, Flavien; Uustalu, Tarmo (2016 年 9 月). グレーディングによる効果と共効果の結合(PDF) . 21st ACM International Conference on Functional Programming. 奈良、日本: Association for Computing Machinery. pp. 476–489. doi :10.1145/2951913.2951939. ISBN 978-1-4503-4219-3。
外部リンク
HaskellWiki 参照:
- 「モナドのすべて」(Jeff Newbern 著) — 一般的なモナドすべてと、それらが Haskell でどのように機能するかについての包括的な説明。「機械化された組立ライン」のアナロジーが含まれています。
- 「Typeclassopedia」(Brent Yorgey 著) — モナドを含む Haskell の主要な型クラスがどのように相互に関連しているかについての詳細な説明。
チュートリアル:
- 「A Fistful of Monads」(オンライン Haskell 教科書Learn You a Haskell for Great Good!より) — ファンクタとアプリカティブ ファンクタ型クラスの出発点から、例を含めてモナドを紹介する章。
- 「さらにいくつかのモナドについて」 —マルコフ連鎖
Probabilityのモナドを含む、より詳しい説明と例を説明する第 2 章。 - 「Functors、Applicatives、および Monads の図解 (Aditya Bhargava 著)」 — モナドに関する簡単でユーモラスなビジュアル チュートリアル。
興味深い事例:
- 「IO モナドとしての UNIX パイプ」(Oleg Kiselyov 著) — Unix パイプが実質的にモナドである理由を説明する短いエッセイ。
- Pro Scala: Web 向けモナド設計パターン(Gregory Meredith 著) — モナドを使用してScalaでの Web 開発のさまざまな側面を改善する方法に関する未発表の完全版原稿。
