モナド(関数型プログラミング)

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

関数型プログラミングではモナドは、関数を構成し、それらの戻り値追加の計算を伴うにラップする構造を持つソフトウェアデザインパターンです。ラッピングモナドタイプの定義に加えて、モナドは2つの演算子を定義します。1つはモナドタイプの値をラップするためのもので、もう1つはモナドタイプの値を出力する関数をまとめるためのものです(これらはモナド関数として知られています)。汎用言語はモナドを使用して定型コードを削減します一般的な操作(未定義の値やフォールブル関数、または簿記コードの処理など)に必要です。関数型言語は、モナドを使用して、関数の複雑なシーケンスを、制御フロー副作用を抽象化する簡潔なパイプラインに変換します。[1] [2]

モナドの概念と用語はどちらも、もともと圏論に由来し、モナドは追加の構造を持つ関手として定義されています。[a] 1980年代後半から1990年代初頭に始まった研究は、モナドが統一された機能モデルの下で一見異なるコンピュータサイエンスの問題をもたらす可能性があることを立証しました。圏論はまた、モナド法として知られるいくつかの正式な要件を提供します。これは、どのモナドでも満たされるべきであり、モナドコードを検証するために使用できます。[3] [4]

モナドは一種の計算に対してセマンティクスを明示的にするため、便利な言語機能を実装するためにも使用できます。Haskellなどの一部の言語は、一般的なモナド構造と一般的なインスタンスのコアライブラリに事前に構築された定義を提供します。[1] [5]

概要

「モナドmの場合、タイプの値は、モナドのコンテキスト内m aでタイプの値にアクセスできることを表します。」a—CAマッキャン[6]

モナドは、型コンストラクター Mと2つの操作return(しばしばユニットとも呼ばれます)を定義することによって作成できます。これは、型の値を受け取り、型コンストラクターを使用して、 それを型のモナド値aにラップします。m a

  • return :: a -> M a

およびbind(通常は)として表されます。これは、型を介し>>=て関数を受け取り、ラップされていない値に適用されるモナディック値を変換して、モナディック値を返すことができます。 fam afaM b

  • bind :: (M a) -> (a -> M b) -> (M b)

(演算子join の代わりに関数を使用する代替の同等の構成 は、後のセクション§ファンクターからの派生にあります。) bind


これらの要素を使用して、プログラマーは一連の関数呼び出し(「パイプライン」)を作成し、いくつかのバインド演算子を式にチェーンします。各関数呼び出しは入力されたプレーンタイプの値を変換し、バインド演算子は返されたモナディック値を処理します。これはシーケンスの次のステップに送られます。

構成された関数呼び出しの各ペアの間に、バインド演算子は、関数内でアクセスできない追加情報を>>=モナディック値に挿入し、パイプラインに渡すことができます。また、特定の条件下でのみ関数を呼び出したり、特定の順序で関数呼び出しを実行したりすることで、実行フローをより細かく制御することもできます。 m af

例:たぶん

モナドの一例はMaybeタイプです。未定義のnull結果は、多くの手続き型言語が処理するための特定のツールを提供しない1つの特定の問題点であり、nullオブジェクトパターンの使用または未定義の値を処理するための各操作で無効な値をテストするためのチェックが必要です。これによりバグが発生し、エラーを適切に処理する堅牢なソフトウェアの構築が困難になります。このMaybe型は、結果の2つの状態を明示的に定義することにより、プログラマーにこれらの潜在的に未定義の結果を処理するように強制しますJust ⌑result⌑Nothingたとえば、プログラマーは、中間結果を返すパーサーを作成している場合や、パーサーが検出した条件を通知し、プログラマーも処理する必要がある場合があります。上にほんの少し余分な機能的なスパイスを加えると、このMaybeタイプは完全な機能を備えたモナドに変わります。[b] :12.3ページ148-151 

ほとんどの言語では、Maybeモナドはオプション型とも呼ばれます。これは、値が含まれているかどうかを示す型です。通常、それらはある種の列挙型として表されます。このRustの例では、これを呼び出します。このタイプのバリアントは、ジェネリック型Maybe<T>の値、または空のバリアントのいずれかになります TNothing

// <T>はジェネリック型 "T"
列挙型を表します 多分< T > { 
    ただT 
    何もない
}

Maybe<T>「ラッピング」タイプとしても理解できます。これは、モナドとの接続が必要になる場所です。ある種のタイプの言語では、モナド関数を相互Maybeに構成したり、モナド関数をテストしたりするなど、使用を支援する関数があります。値が含まれています。 Maybe

次のハードコードされた例では、失敗する可能性のある関数の結果として型が使用されます。この場合、ゼロ除算Maybeがある場合、型は何も返しません

fn  split x10進数y10進数 ->たぶん< 10進数> {   
    y == 0の場合{何も返さない}       
    else { return Just x / y }      
}
//divide(1.0、4.0)-> Just(0.25)
を返します//divide(3.0、0.0)->Nothingを返します

Maybeに値が含まれているかどうかをテストするそのような方法の1つは、ifステートメントを使用することです。

m_x =除算3.14、0.0 ; _ _ //上記の除算関数を参照してください// ifステートメントはm_xからxを抽出しますifm_xがMaybeのJustバリアントである場合Just x = m_x {     

     
    印刷"回答:" x  
} else {  
    印刷「除算に失敗しました。ゼロ除算エラー...」
}

他の言語ではパターンマッチングが行われる場合があります

結果=除算3.0、2.0 ; _ _    
一致結果{  
    Just x => print "answer:" x )、   
    Nothing => print "除算に失敗しました。次回はemを取得します。" )、  
}

Maybeモナドは、一緒に戻る関数を構成できます。これを行う具体的な例の1つは、1つの関数にsを取り込んで、次のようなもの Maybeを返すことです。Maybe

fn  chainable_division maybe_xたぶん< Decimal > maybe_yたぶん< Decimal >  ->たぶん< Decimal > {   
    一致maybe_x maybe_y {   
        Just x )、Just y ))=> { //両方の入力がJustの場合、除算を返す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を返します   

この具体的な例で多分取るために関数を書き直さなければならないことは、多くの定型文を必要とします(それらすべてのJust式を見てください!)。代わりに、バインド演算子と呼ばれるものを使用できます。(「map」、「flatmap」、または「shove」とも呼ばれます[8] :2205s )。この操作は、モナドと、モナドを返す関数を受け取り、渡されたモナドの内部値に対して関数を実行し、関数からモナドを返します。

//「。map」を使用したRustの例。may_xは、Maybe <Decimal>とMaybe <String>をそれぞれ返す2つの関数を介して渡されます。
//通常の関数合成と同様に、相互にフィードする関数の入力と出力は、ラップされたタイプと一致する必要があります。(つまり、add_one関数はMaybe <Decimal>を返す必要があります。これは、decimal_to_string関数のDecimalにアンラップできます)
let may_xMaybe < Decimal > = Just 1.0    
多分_結果=多分_xとますマップ| x | add_one x ))。マップ| x | decimal_to_string x ))     

Haskellには、演算子のバインド、または(>>=)があり、関数の合成に似たよりエレガントな形式でこのモナディック合成を可能にします。[c] :150〜151 

halve  ::  Int-  > たぶん Inthalve 
x | _  x = Just x ` div` 2 | _ _ odd x = Nothing-このコードはxを2回半分にします。xが4の倍数でない場合はNothingと評価されますx >> = halve
         
       
 
   

>>=利用可能であると、無名関数(つまりラムダ)chainable_divisionの助けを借りてはるかに簡潔に表現することができます。以下の式で、2つのネストされたラムダがそれぞ​​れ、バインド演算子を使用して、渡されたモナドのラップされた値に対してどのように動作するかに注意してください。[d] :93 Maybe

 chainable_division mx my  =    mx  >> =    λx-  >    my  >> =  λy-  >  Just  x  /  y ))   

これまでに示したのは基本的にモナドですが、より簡潔にするために、次のセクションで定義するモナドに必要な品質の厳密なリストを以下に示します。

モナディックタイプ
タイプ(Maybe[b] :148–151 
単位操作
型変換器(Just(x)[d] :93 
バインド操作
モナディック関数のコンビネータ(>>=または.map()[c] :150–151 

これらは、モナドを形成するために必要な3つのものです。他のモナドは異なる論理プロセスを具体化する場合があり、一部には追加のプロパティがある場合がありますが、それらすべてにこれら3つの類似したコンポーネントがあります。[1] [9]

定義

上記の例で使用されている関数型プログラミングのモナドのより一般的な定義は、実際には圏論の標準的な定義ではなく、クライスリ圏のトリプルに基づいています。ただし、2つの構成は数学的に同等であることが判明したため、どちらの定義でも有効なモナドが生成されます。明確に定義された基本タイプTUが与えられると、モナドは3つの部分で構成されます。

  • モナディック型MTを構築する型コンストラクター M [e]
  • モナドにオブジェクトxを埋め込む型コンバーター(しばしばユニットまたはリターンと呼ばれる) :
    unit : T → M T[f]
  • 通常はbind呼ばれ(変数をバインドする場合のように)、中置演算子またはflatMapと呼ばれるメソッドで表されるコンビネータは、モナディック変数をアンラップし、それをモナディック関数/式に挿入して、新しいモナディック値を生成します。 >>=
    (>>=) : (M T, T → M U) → M U[g]そうならmx : M T、そしてf : T → M U (mx >>= f) : M U

ただし、モナドとして完全に適格となるには、これら3つの部分もいくつかの法則を尊重する必要があります。

  • unitbindの左IDです
    unit(x) >>= f f(x)
  • ユニットバインドの正しいIDでもあります:
    ma >>= unit ma
  • バインドは本質的に結合的です:[h]
    ma >>= λx → (f(x) >>= g) (ma >>= f) >>= g[1]

代数的に、これは、任意のモナドがカテゴリ(クライスリ圏と呼ばれる)関手圏のモノイド(値から計算まで)の両方を生成し、モノイドの二項演算子としてのモノイド合成[8] :2450s 単位を意味します。モナドのアイデンティティとして。

使用法

モナドパターンの価値は、単にコードを凝縮し、数学的推論へのリンクを提供するだけではありません。開発者が使用する言語またはデフォルトのプログラミングパラダイムが何であれ、モナドパターンに従うことで、純粋関数型プログラミングの多くの利点がもたらされます。特定の種類の計算を具体化することにより、モナドはその計算パターンの面倒な詳細をカプセル化するだけでなく、宣言的な方法でカプセル化し、コードの明確さを向上させます。モナディック値は計算値だけでなく計算効果も明示的に表すため、モナディック式は参照透過性の位置にある値に置き換えることができます。、純粋な式と同じように、書き換えに基づく多くの手法と最適化が可能になります。[4]

通常、プログラマーはバインドを使用してモナド関数をシーケンスにチェーンします。これにより、モナドを「プログラム可能なセミコロン」と表現する人もいます。これは、ステートメントを区切るためにセミコロンを使用する命令型言語の数を示しています。[1] [5] ただし、モナドは実際には計算を順序付けないことを強調しておく必要があります。それらを中心的な機能として使用する言語でも、より単純な関数合成により、プログラム内のステップを配置できます。モナドの一般的な有用性は、プログラムの構造を単純化し、抽象化によって関心の分離を改善することにあります。[4] [11]

モナド構造は、デコレータパターンの独自の数学的およびコンパイル時の変化と見なすこともできます一部のモナドは、関数にアクセスできない追加のデータを渡すことができます。また、特定の条件下でのみ関数を呼び出すなど、実行をより細かく制御できるモナドもあります。アプリケーションプログラマーがボイラープレートコードを事前に開発されたモジュールにオフロードしながらドメインロジックを実装できるため、モナドはアスペクト指向プログラミングのツールと見なすこともできます[12]

モナドのもう1つの注目すべき使用法は、入力/出力や可変状態などの副作用を、純粋関数型コードで分離することです。純粋関数型言語でさえ、特に関数の合成と継続渡しスタイル(CPS)の複雑な組み合わせにより、モナドなしでこれらの「不純な」計算を実装できます。[2] ただし、モナドを使用すると、基本的にCPSコードの各繰り返しパターンを取得し、それを個別のモナドにバンドルすることで、このスキャフォールディングの多くを抽象化できます。[4]

言語がデフォルトでモナドをサポートしていない場合でも、パターンを実装することは可能ですが、多くの場合、それほど困難はありません。圏論からプログラミング用語に翻訳すると、モナド構造は一般的な概念であり、有界ポリモーフィズムの同等の機能をサポートする任意の言語で直接定義できます基礎となる型に取り組んでいる間、操作の詳細にとらわれない概念の能力は強力ですが、モナドのユニークな機能と厳格な振る舞いは、他の概念とは一線を画しています。[13]

アプリケーション

特定のモナドは特定の計算形式を表すため、特定のモナドの説明では、通常、狭い実装問題の解決に焦点を当てます。ただし、状況によっては、アプリケーションは、コアロジック内で適切なモナドを使用することで、高レベルの目標を達成することもできます。

デザインの中心にモナドがあるアプリケーションのほんの一部を次に示します。

歴史

プログラミングにおける「モナド」という用語は、実際にはAPLおよびJプログラミング言語にまでさかのぼります。これらの言語は、純粋に機能する傾向があります。ただし、これらの言語では、「モナド」は1つのパラメーターを受け取る関数(2つのパラメーターが「ダイアド」である関数など)の省略形にすぎません。[19]

数学者のロジャー・ゴデメンは、1950年代後半にモナド(「標準構造」と呼ばれる)の概念を最初に考案しましたが、支配的になった「モナド」という用語は、カテゴリー理論家のソーンダース・マックレーンによって普及しました。[要出典]ただし、 bindを使用して上記で定義された形式は、モナドが2つの(共変)関手の間の随伴として特徴付けられることを証明するために、数学者ハインリッヒクライスリによって1965年に最初に記述されました。[20]

1980年代から、モナドパターンの漠然とした概念がコンピュータサイエンスコミュニティで表面化し始めました。プログラミング言語の研究者PhilipWadlerによると、コンピューター科学者のJohn C. Reynoldsは、1970年代から1980年代初頭に、継続渡しスタイルの価値、形式的意味論の豊富な情報源としての圏論、およびタイプについて議論したときに、そのいくつかの側面を予測しました。値と計算の違い。[4] 1990年まで積極的に設計された 研究言語オパールも、効果的にモナディック型に基づいたI / Oでしたが、当時は接続が実現されていませんでした。[21]

コンピューター科学者のEugenioMoggiは、1989年の会議論文[22]で、圏論のモナドを関数型プログラミングに明示的にリンクした最初の人物であり、その後1991年にさらに洗練されたジャーナルが提出されました。ラムダ計算のセマンティクスを提供する圏論Moggiの重要な洞察は、実際のプログラムは値から他の値への関数であるだけでなく、それらの値の計算を形成する変換であるということでした。圏論の用語で形式化すると、これはモナドがこれらの計算を表す構造であるという結論につながります。[3]

フィリップ・ワドラーやサイモン・ペイトン・ジョーンズなど、他のいくつかのアイデアが普及し、このアイデアに基づいて構築されました。どちらもハスケルの仕様に関与していました。特に、Haskellはv1.2までの問題のある「遅延ストリーム」モデルを使用して、より柔軟なモナディックインターフェイスに切り替えるまで、 I / Oと遅延評価を調整していました。[23] Haskellコミュニティは、関数型プログラミングの多くの問題にモナドを適用し続け、2010年代に、Haskellと協力している研究者は、モナドが適用可能なファンクターであることを最終的に認識しました。[24] [i]そしてモナドとの両方がモノイドであること。[26]

当初、モナドを使用したプログラミングは主にHaskellとその派生語に限定されていましたが、関数型プログラミングが他のパラダイムに影響を与えたため、多くの言語でモナドパターンが組み込まれました(名前ではないにしても精神的に)。現在、 SchemePerlPythonRacketClojureScalaF#に定式化が存在し、新しいML標準としても検討されています。[要出典]

分析

モナドパターンの利点の1つは、プログラムロジックに数学的な精度をもたらすことです。モナドの法則を使用してインスタンスの有効性を確認できるだけでなく、関連する構造(ファンクターなど)の機能をサブタイピングで使用できます。

モナド法則の検証

例に戻るとMaybe、そのコンポーネントはモナドを構成すると宣言されましたが、モナドの法則を満たしているという証拠はありませんでした。

これは、の詳細を一般法の一方の側に差し込んでMaybeから、代数的に等式のチェーンを構築してもう一方の側に到達する ことで修正できます。

法則1:   eta(a)>> = f(x)⇔(ちょうどa)>> = f(x)⇔f(a)
法則2:   ma >> = eta(x)⇔ma

        ma(ちょうどa)場合
            eta(a)⇔ただ
        そうでなければ                        または
            何もない⇔何もない
        終了する場合
法則3 :(   ma >> = f(x) >> = g(y)⇔ma>> =  f(x)>> = g(y)

        if(ma >> = f(x))is(Just b)then                if ma is(Just a)then
            g(ma >> = f(x))(f(x)>> = g(y))a
                                                    それ以外の場合
            何も何も
        終了する場合                                          終了する場合

                ⇔ma  (Just a)f(a)(Just b)の場合      
                       (g∘f)a
                   それ以外の場合、 ma(Just a)で、 f(a)がNothingの場合
                       何もない
                   そうしないと
                       何もない
                   終了する場合

ファンクターからの派生

コンピュータサイエンスではまれですが、圏論を直接使用できます。これは、モナド2つの追加の自然変換を伴う関手として定義します。したがって、最初に、構造体は、ファンクターとしての資格を得るために、 mapという名前の高階関数(または「関数」)を必要とします。

map φ : (a → b) → (ma → mb)

ただし、これは必ずしも大きな問題ではありません。特に、モナドが既存のファンクターから派生している場合、モナドはマップを自動的に継承します。(歴史的な理由から、これmapは代わりfmapにHaskellで呼ばれています。)

モナドの最初の変換は、実際にはクライスリトリプルと同じユニットですが、構造の階層を厳密にたどると、ユニットは、モナドと基本ファンクターの中間構造である適用ファンクターを特徴付けることがわかります。適用可能なコンテキストでは、ユニットは純粋と呼ばれることもありますが、それでも同じ機能です。この構造の違いは、法務部門が満たさなければならないことです。バインドが定義されていないため、代わり にマップに関して制約が与えられます。

(unit ∘ φ) x ↔ ((map φ) ∘ unit) x[27]

アプリケーションファンクターからモナドへの最後の飛躍は、2番目の変換である結合関数(圏論ではこれは通常μと呼ばれる自然変換です)であり、モナドのネストされたアプリケーションを「フラット化」します。

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

開発者が直接モナドまたはクライスリトリプルを定義するかどうかに関係なく、基本的な構造は同じであり、フォームは互いに簡単に導出できます。

(map φ) ma ↔ ma >>= (unit ∘ φ)
join(mma) ↔ mma >>= id
ma >>= f ↔ (join ∘ (map f)) ma[28]

別の例:リスト

リストモナド、より単純なファンクターからモナドを導出することがどのように役立つかを自然に示しています。多くの言語では、リスト構造はいくつかの基本機能とともに事前定義されているため、List型コンストラクターと追加演算子(++中置記法で表されます)は、ここですでに示したとおりに想定されています。

リストにプレーンな値を埋め込むことも、ほとんどの言語で簡単です。

unit(x)= [x]

ここから、リスト内包表記を使用して関数を繰り返し適用することは、リストをバインドして完全なモナドに変換するための簡単な選択のように思えるかもしれません。このアプローチの難しさは、bindがモナディック関数を期待していることです。この場合、モナディック関数はリスト自体を出力します。より多くの機能が適用されると、ネストされたリストのレイヤーが蓄積され、基本的な理解以上のものが必要になります。

ただし、リスト全体、つまりマップに単純な関数を適用する手順は簡単です。

(マップφ)xlist = [φ(x1)、φ(x2)、...、φ(xn)]

現在、これらの2つの手順は、すでにListアプリケーションファンクターに昇格しています。モナドとして完全に認定するには、繰り返される構造をフラット化するための結合の正しい概念のみが必要ですが、リストの場合、値を含む内部リストを追加するために外部リストをアンラップすることを意味します。

join(xlistlist)= join([xlist1、xlist2、...、xlistn])
                 = xlist1 ++ xlist2 ++ ... ++ xlistn

結果のモナドはリストであるだけでなく、関数が適用されると自動的にサイズ変更されて凝縮されます。 bindは、数式だけで導出できるようになりList、モナディック関数のパイプラインを介して値 をフィードするために使用できるようになりました。

モナドは、List複素数の根などの多値関数の使用を大幅に簡素化できます。[29]
(xlist >> = f)=参加∘(マップf)xlist

このモナディックリストの1つのアプリケーションは、非決定論的計算を表すことです。 Listアルゴリズム内のすべての実行パスの結果を保持し、各ステップでそれ自体を凝縮して、どのパスがどの結果につながったかを「忘れる」ことができます(決定論的で網羅的なアルゴリズムとの重要な違いもあります)。[要出典] もう1つの利点は、小切手をモナドに埋め込むことができることです。特定のパスは、パイプライン内の関数を書き直す必要なしに、最初の障害点で透過的にプルーニングできます。[28]

List輝く2番目の状況は、多値関数の作成です。たとえば、数値のn番目の複素数ルートはn個の異なる複素数を生成する必要がありますが、それらの結果から別のm番目のルートを取得する場合、最終的なm•n値はm•n番目のルートの出力と同じである必要があります。 Listこの問題を完全に自動化し、各ステップの結果をフラットで数学的に正しいリストにまとめます。[29]

テクニック

モナドは、プログラムロジックを整理するだけでなく、興味深い手法の機会を提供します。モナドは、有用な構文機能の基礎を築くことができますが、その高レベルで数学的な性質により、重要な抽象化が可能になります。

シンタックスシュガーdo-notation

bindを公然と使用することはしばしば理にかなっていますが、多くのプログラマーは必須のステートメントを模倣する構文を好みます(Haskellではdo-notationOCamlではperform-notationF#では計算式[30]Scalaでは理解のためこれは、モナディックパイプラインをコードブロックとして偽装する唯一のシンタックスシュガーです。その後、コンパイラはこれらの式を基礎となる関数型コードに静かに変換します。

add関数をからMaybeHaskellに変換すると、この機能の動作を示すことができます。Haskellの非モナディックバージョンはadd次のようになります。

add  mx  my  = 
    case  mx  of 
        Nothing- > Nothing  Just x- > case my of Nothing- > Nothing Just y- > Just x + y  
              
                         
                              

モナドHaskellでは、はユニットreturnの標準名であり、ラムダ式は明示的に処理する必要がありますが、これらの技術を使用しても、モナドはより明確な定義になります。 Maybe

add  mx  my  = 
    mx  >> =  \ x-  > 
        my  >> =  \ y-  > 
            return  x  +  y )))

ただし、do表記を使用すると、これをさらに直感的なシーケンスにまとめることができます。

add  mx  my  =  do 
    x  <  -mx 
    y  <  -my 
    return  x  +  y 

2番目の例はMaybe、まったく異なる言語であるF#での使用方法を示しています。None計算式では、未定義のオペランドまたはゼロによる除算を返す「安全な除算」関数は、次のように記述できます。

let  readNum  () = 
  let  s  =  Console ReadLine ()
  let  succ v  =  Int32 TryParse s 
  if  suc  then  Some v  else  None

 secure_div  = 
  多分 {  
    let _ x = readNum ()let y = readNum ()if y = 0 then None else return x / y }   
       
        
     
        
  

ビルド時に、コンパイラーはこの関数を内部的にバインド呼び出し のより密なチェーンに「デシュガー」します。

多分Delay fun   - 
  >多分。Bind readNum fun x- >多分。Bind readNum fun y- > if y = 0 thenありませ_ _ )。   
       
             

最後の例として、一般的なモナド法自体でさえ、表記法で表すことができます。

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  }

開発者は便利ですが、このブロックスタイルは純粋に構文的であり、外向きのモナディック(または非モナディックCPS)式に置き換えることができることを常に覚えておく必要があります。多くの場合、 bindを使用してモナディックパイプラインを表現する方が明確です。一部の関数型プログラミングの支持者は、ブロックスタイルでは初心者が命令型プログラミングから習慣を引き継ぐことができるため、デフォルトでは回避し、明らかに優れている場合にのみ使用する必要があると主張しています。[31] [1]

一般的なインターフェース

すべてのモナドには、モナドの法則を満たす特定の実装が必要ですが、言語内の他の構造や標準イディオムとの関係などの他の側面は、すべてのモナドで共有されます。その結果、言語またはライブラリは、関数プロトタイプ、サブタイピング関係、およびその他の一般的な事実を備えた一般的なMonadインターフェイスを提供する場合があります。開発の先頭に立ち、新しいモナドがスーパータイプ(ファンクターなど)から機能を継承することを保証することに加えて、インターフェースに対してモナドの設計をチェックすることで、品質管理の別のレイヤーが追加されます。[要出典]

演算子

モナディックコードは、演算子を慎重に使用することで、さらに単純化できることがよくあります。マップ機能は、アドホックなモナディック関数以上のもので機能するため、特に役立ちます。モナディック関数が事前定義された演算子と同様に機能する必要がある限り、マップを使用して、より単純な演算子をモナディック演算子に即座に「持ち上げる」ことができます。[j] この手法を使用すると、例addからの定義を次のMaybeように抽出できます。

add(mx、my)=マップ(+)

addだけでMaybeなく、インターフェイス全体を定義することで、このプロセスをさらに一歩進めることができMonadます。これを行うことにより、構造インターフェースに一致し、独自のマップを実装する新しいモナドは、リフトされたバージョンもすぐに継承しaddます。必要な関数への唯一の変更は、型シグネチャを一般化することです。

追加:(モナド番号、モナド番号)→モナド番号[32]

分析にも役立つもう1つのモナディック演算子は、モナディック構成(>=>ここでは中置として表されます)です。これにより、モナディック関数をより数学的なスタイルで連鎖させることができます。

(f> => g)x =(f(x)→mb)>> = g(y = b)

この演算子を使用すると、モナドの法則を関数のみで記述でき、結合性とアイデンティティの存在への対応を強調できます。

(単位> => 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モナドであり、モナドの法則を満たすために単純な値と関数に注釈を付けるだけです。

newtype Id T = T

unit(x)= x
(x >> = f)= f(x)

Identityただし、再帰モナド変換子のベースケースを提供するなど、実際には有効な用途がありますまた、命令型ブロック内で基本的な変数の割り当てを実行するために使用することもできます。[k] [要出典]

コレクション

適切な追加が付いたコレクションはすでに自由モノイドですが、明確に定義された結合を持ち、モナドとしての資格を持つコレクションListはそれだけではないことがわかりました。追加に特別なプロパティを課すだけで、これらの他のモナディックコレクションに変更することもできます[l] [要出典]List

コレクション モノイドの特性
リスト 無料
有限多重集合 可換
有限集合 可換でべき等
有限の順列 非可換でべき等

IOモナド(Haskell)

すでに述べたように、純粋なコードには管理されていない副作用があってはなりませんが、それはプログラムが効果を明示的に記述して管理することを妨げるものではありません。このアイデアは、HaskellのIOモナドの中心であり、タイプのオブジェクトはIO a、プログラム外の世界の現在の状態を含み、タイプの値を計算していると見なすことができますa値を計算しない計算(つまり、プロシージャ)のタイプIO ()は、ダミー値を「計算」します()プログラマーがIO値を関数にバインドすると、関数はその世界観(ユーザー、ファイルなどからの入力)に基づいて決定を下し、新しい世界状態(プログラム出力)を反映するモナディック値を生成します。[23]

たとえば、Haskellには、ファイルが存在するかどうかをチェックする機能やファイルを削除する機能など、より広いファイルシステムに作用するためのいくつかの機能があります。それらの2つの型署名は次のとおりです。

doesFileExist  ::  FilePath-  >  IO  Bool 
removeFile  ::  FilePath-  >  IO  ()

1つ目は、特定のファイルが実際に存在するかどうかに関心があり、その結果、モナド内にブール値を出力します。IO一方、2番目の関数は、ファイルシステムでの動作のみに関係しているため、IO出力されるコンテナは空です。

IOただし、ファイルI / Oだけに限定されません。ユーザーI / Oも可能であり、命令型のシンタックスシュガーとともに、典型的な「Hello、World!」を模倣できます。プログラム

main  ::  IO  ()
main  =  do 
  putStrLn  "Hello、world!" 
  putStrLn  「あなたの名前は何ですか、ユーザー?」
  name  <  -getLine 
  putStrLn  "はじめまして、"  ++  name  ++  "!" 

脱糖すると、これは次のモナディックパイプラインに変換されます( Haskellでは、モナディック効果のみが重要であり、基になる結果を破棄できる場合 のバインド>>の変形にすぎません):

main  ::  IO  ()
main  = 
  putStrLn  "Hello、world!"  >> 
  putStrLn  「あなたの名前は何ですか、ユーザー?」 >>  
  getLine  >> =  \ name-  > 
    putStrLn  "はじめまして、"  ++  name  ++  "!" ))

ライターモナド(JavaScript)

もう1つの一般的な状況は、ログファイルを保持するか、プログラムの進行状況を報告することです。プログラマーは、後でプロファイリングまたはデバッグするために、さらに具体的な技術データをログに記録したい場合がありますWriterモナドは、段階的に蓄積される補助出力を生成することにより、これらのタスクを処理できます。

モナドパターンが主に関数型言語に制限されていないことを示すために、この例ではJavaScriptWriterでモナドを実装していますまず、配列(ネストされたテールを持つ)を使用すると、型をリンクリストとして作成できます。基になる出力値は配列の位置0に存在し、位置1は暗黙的に一連の補助ノートを保持します。 Writer

const  writer  =  value  =>  [ value  []];

単位の定義も非常に簡単です。

const  unit  =  value  =>  [ value  []];

デバッグノート付きのオブジェクトを 出力する単純な関数を定義するために必要なのは、ユニットのみです。Writer

const  squared  =  x  =>  [ x  *  x  [ ` $ { x }は2乗されました。` ]]; 
const  halved  =  x  =>  [ x  /  2  [ ` $ { x }は半分になりました。` ]];

真のモナドにはまだバインドが必要ですが、の場合Writer、これは単に関数の出力をモナドのリンクリストに追加することになります。

const  bind  =  writer  transform  =>  { 
    const  [ value  log ]  =  writer ; 
    const  [ result  updates ]  =  transform value ); 
    [結果ログを返します concat 更新)]; }; 

サンプル関数はbindを使用してチェーン化できるようになりましたが、モナディックコンポジションのバージョン(pipelogここでは呼ばれます)を定義すると、これらの関数をさらに簡潔に適用できます。

const  pipelog  =  writer  ... transforms  => 
    transforms 削減バインド ライター);

最終的な結果は、計算をステップスルーすることと、後で監査するためにそれらをログに記録することの間の関心の分離です。

パイプログ単位4 )、 二乗 半分); 
//結果のライターオブジェクト= [8、['4は二乗されました。'、 '16は半分になりました。']]

環境モナド

環境モナド(リーダーモナドおよび関数モナドとも呼ばれます)を使用すると、計算を共有環境の値に依存させることができます。モナド型コンストラクターは、型Tを型ETの関数にマップします。ここで、Eは共有環境の型です。モナド関数は次のとおりです。

次のモナディック操作が役立ちます。

ask操作は現在のコンテキストを取得するために使用され、localは変更されたサブコンテキストで計算を実行します。状態モナドの場合と同様に、環境モナドでの計算は、環境値を提供し、それをモナドのインスタンスに適用するだけで呼び出すことができます。

正式には、環境モナドの値は、追加の匿名引数を持つ関数と同等です。returnbindは、 SKIコンビネータ計算のKコンビネータとSコンビネータにそれぞれ相当します。

州のモナド

状態モナドを使用すると、プログラマーは任意のタイプの状態情報を計算に添付できます。任意の値型が与えられると、状態モナドの対応する型は、状態を受け入れ、(型のs)戻り値とともに新しい状態(型の)を出力する関数tです。これは環境モナドに似ていますが、新しい状態を返すため、可変環境のモデリングが可能になる点が異なります。

タイプ States  t = s  - > t s      

このモナドは、状態情報のタイプであるタイプパラメーターを受け取ることに注意してください。モナド操作は次のように定義されます。

-"return"は、状態を変更せずに指定された値を生成します。
return  x  =  \ s  ->  x  s 
-"bind"は、結果にfを適用するようにmを変更します。
m  >> =  f  =  \ r-  >  let  x  s  =  m  r  in  f  x  s

便利な状態操作は次のとおりです。

get  =  \ s  ->  s  s  -計算のこの時点での状態を調べます。
put  s  =  \ _-  >  () s  -状態を置き換えます。
変更 f  =  \ s  ->  () f  s  -状態を更新します

別の操作は、状態モナドを特定の初期状態に適用します。

runState  :: 状態 sa- > s  - > a  s runState t s = t s    
     

状態モナドのdo-blockは、状態データを調べて更新できる一連の操作です。

非公式には、状態タイプSの状態モナドは、戻り値Tのタイプをタイプの関数にマップします。、ここで、Sは基礎となる状態です。returnおよびbind関数は次 のとおりです。

圏論の観点から、状態モナドは、積関数と指数関数の間の随伴から導出されます。これは、定義上、デカルト閉圏に存在します。

継続モナド

戻り型R継続モナドは、型Tを型の関数にマップします継続渡しスタイルをモデル化するために使用されます。return関数とbind関数は次のとおりです。

call-with-current-continuation関数は、次のように定義されています。

プログラムロギング

次のコードは擬似コードです。2つの関数foobar、タイプがあるとします。

foo   int-  >  int 
bar   int-  >  int

つまり、両方の関数が整数を受け取り、別の整数を返します。次に、次のように関数を連続して適用できます。

foo  bar  x 

ここで、結果は、にfoo適用された結果にbar適用された結果ですx

ただし、プログラムをデバッグしていて、ログメッセージをfooとに追加したいとしbarます。したがって、タイプを次のように変更します。

foo   int-  >  int  * 文字列
bar   int-  >  int  *  string

そのため、両方の関数は、アプリケーションの結果を整数として、適用された関数と以前に適用されたすべての関数に関する情報を文字列として含むログメッセージを含むタプルを返します。

残念ながら、これは、入力タイプが出力タイプfoo互換性がないため、作成できなくなったことを意味しますまた、各関数の型を変更することで構成可能性を得ることができますが、これには各関数にボイラープレートコードを追加してタプルから整数を抽出する必要があり、そのような関数の数が増えると面倒になります。 barintint * stringint * string -> int * string

代わりに、この定型文を抽象化するためのヘルパー関数を定義しましょう。

bind   int  *  string  ->  int-  >  int  *  string  ->  int  *  string

bind整数と文字列のタプルを受け取り、次にfoo整数から整数と文字列のタプルにマップする関数(のような)を受け取ります。その出力は整数と文字列のタプルです。これは、入力関数を入力整数と文字列のタプル内の整数に適用した結果です。このように、ボイラープレートコードを記述して、タプルから整数を1回だけ抽出する必要がありbindます。

今、私たちはいくつかの構成可能性を取り戻しました。例えば:

bind  bind  x s  bar  foo

(x,s)整数と文字列のタプルは どこにありますか。

利点をさらに明確にするために、中置演算子を次のエイリアスとして定義しましょうbind

>> =   int  *  string  ->  int-  >  int  *  string  ->  int  *  string

つまり、それt >>= fはと同じbind t fです。

次に、上記の例は次のようになります。

((x s  >> =  bar  >> =  foo

(x, "")最後に、空のログメッセージを作成するたびに書き込む必要がないようにすると便利です。ここ""で、は空の文字列です。それでは、新しい関数を定義しましょう。

return   int-  >  int  *  string

x上記のタプルで ラップします。

これで、メッセージをログに記録するための優れたパイプラインができました。

((return  x  >> =  bar  >> =  foo

barこれにより、の効果をfooより簡単に記録できますx

int * stringモナディック値に類似していますbindおよびreturnは、同じ名前の対応する関数に類似しています。実際int * string、、、bindおよびreturnはモナドを形成します。

バリエーション

数学的なレベルでは、いくつかのモナドは特に優れた特性を持ち、特定の問題に独自に適合しています。

加法モナド

加法モナドは、追加の閉じた結合法則の二項演算子mplusと、mplusの下にあるmzeroと呼ばれる単位元を備えたモナドです。モナドは、mzeroとして、 OR演算子のバリエーションをmplusとして、加法と見なすことができますは加法モナドでもあり、空のリストはmzeroとして機能し、連結演算子mplusとして機能します。 MaybeNothingList[]++

直感的には、mzeroは、基になる型からの値がないモナディックラッパーを表しますが、バインドのアブソーバーとして機能し、モナディック関数にバインドされるたびにmzeroを返すため、(「1」ではなく)「ゼロ」と見なされます。このプロパティは両側であり、いずれかの値がモナディックゼロ関数にバインドされると、 bindmzeroを返します。

圏論の用語では、加法モナドは、バインドを使用したモナド関数(すべてのモナドがそうであるように)に対してモノイドとして一度適格となり、 mplusを介してモナド値に対して再び適格になります[33] [m]

無料のモナド

モナドの一般的なアウトラインが役立つ場合もありますが、1つのモナドまたは別のモナドを推奨する単純なパターンはありません。ここで無料のモナドが登場します。モナドのカテゴリーの自由対象として、モナド法自体を超える特定の制約なしにモナド構造を表すことができます。自由モノイドが評価なしで要素を連結するのと同じように、自由モノイドは型システムを満たすためにマーカーを使用して計算を連鎖させることができますが、それ以外の場合は、それ自体はより深いセマンティクスを課しません。

たとえば、マーカーJustNothingマーカーを完全に処理することにより、Maybeモナドは実際には無料のモナドになります。一方List、モナドは、リストに関する追加の特定のファクト(appendなど)を定義に含めるため、無料のモナドではありません。最後の例は、抽象的な無料のモナドです。

data  Free  f  a 
  =  Pure  a 
  |  無料 f  無料 f  a ))

ユニット ::  a-  > フリー fa ユニット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 

ただし、無料のモナドは、この例のようにリンクリストに制限されておらず、のような他の構造の周りに構築できます

無料のモナドを意図的に使用することは、最初は実用的ではないように思われるかもしれませんが、その形式的な性質は、構文上の問題に特に適しています。無料のモナドを使用して、構文と型を追跡し、後でセマンティクスを残すことができます。その結果、パーサーとインタープリターで使用できるようになりました。[34]他の人は、言語内で反復 を提供するなど、より動的で運用上の問題にもそれらを適用しました[35]

コモナッド

追加のプロパティを持つモナドを生成することに加えて、任意のモナドに対して、comonadを定義することもできます。概念的には、モナドが基礎となる値から構築された計算を表す場合、コモナドは値への還元と見なすことができます。ある意味で、モナディックコードを完全に「解凍」することはできません。値がモナド内にラップされると、その値は副作用とともにそこで隔離されたままになります(純粋関数型プログラミングでは良いことです)。ただし、問題は、comonadsが明示的にモデル化できるコンテキストデータの消費に関するものである場合もあります。

技術的には、comonadはモナドのカテゴリの双対です。これは、型署名の方向がになっている場合にのみ、同じ必要なコンポーネントを持つことを大まかに意味しますバインド中心のモナド定義から始めて、comonadは次のもので構成されます。

  • 高次型WTをマークする型コンストラクターW
  • ここではcounitと呼ばれるユニットのデュアルは、comonadから基本的な値を抽出します。
counit(wa):WT→T
  • 一連の還元機能を拡張するバインドの反転(で表される=>>) :
(wa = >> f):( WU、WU→T)→WT [n]

拡張共同ユニットは、モナド法の二重性も満たさなければなりません。

counit∘ (wa = >> f)→wb  ↔f   (wa)→b
wa = >>counit↔wa
wa (= >> f(wx = wa))→wb(= >> g(wy = wb))→wc  wa(= >> f(wx = wa))→wb (= >> g(wy = wb))→wc

モナドと同様に、コモナドは、結合のデュアルを使用してファンクターから派生させることもできます。

  • 複製はすでにcomonadic値を取り、それをcomonadic構造の別のレイヤーでラップします。
複製(wa):WT→W(WT)

ただし、extendなどの操作は逆になりますが、comonadは作用する関数を逆にしないため、 comonadは、コファンクターではなく、マップを使用したファンクターのままです。重複counit、およびmapを使用した代替定義も、独自のコモナド法を尊重する必要があります。

((マップ複製)∘複製)wa↔(複製∘複製)wa↔wwwa
((マップコユニット)∘重複)wa↔(コユニット∘重複)wa↔wa
((マップマップφ)∘重複)wa↔(重複∘(マップφ))wa↔wwb

モナドと同様に、2つの形式は自動的に変換できます。

(マップφ)wa↔wa= >>(φ∘counit)wx
重複wa↔wa= >> wx
wa = >> f(wx)↔((map f)∘duplicate)wa

簡単な例は、入力値と共有環境データに基づいて値を出力するProductcomonadです。実際、ProductコモナドはモナドのデュアルでありWriter、事実上モナドと同じReaderです(両方とも以下で説明します)。 ProductまたReader、受け入れる関数シグネチャと、値をラップまたはアンラップすることによってこれらの関数を補完する方法のみが異なります。

それほど簡単ではない例は、Stream comonadです。これは、データストリームを表し、 extendを使用して着信信号にフィルターをアタッチするために使用できます実際、モナドほど人気はありませんが、研究者は、コモナドがストリーム処理とデータフロープログラミングのモデリングに特に役立つことを発見しました。[36] [37]

ただし、厳密な定義のため、モナドとコモナドの間でオブジェクトを単純に前後に移動することはできません。さらに高度な抽象化として、矢印は両方の構造を包含することができますが、モナディックコードとコモナディックコードを組み合わせるためのよりきめ細かい方法を見つけることは、活発な研究分野です。[38] [39]

も参照してください

モデリング計算の代替案:

関連する設計コンセプト:

  • アスペクト指向プログラミングは、モジュール性と単純さを向上させるために、補助的な簿記コードを分離することを強調しています
  • 制御の反転は、包括的なフレームワークから特定の関数を呼び出すという抽象的な原則です。
  • 型クラスは、Haskellでモナドやその他の構造を実装するために使用される特定の言語機能です。
  • デコレータパターンは、オブジェクト指向プログラミングで同様の利点を実現するための、より具体的なアドホックな方法です。

モナドの一般化:

  • 適用可能なファンクターは、それをマップに関連付ける単位と法則のみを保持することにより、モナドから一般化します。
  • 矢印は追加の構造を使用して、プレーンな関数とモナドを単一のインターフェイスにまとめます
  • モナド変換子は、別個のモナドに作用して、それらをモジュール式に結合します

メモ

  1. ^ プログラミングでは複数の自由変数の関数が一般的であるため、この記事で説明するモナドは、技術的には、カテゴリ理論家が強力なモナドと呼ぶです。[3]
  2. ^ a b たぶんの具体的な動機は(Hutton 2016)にあります。[7]
  3. ^ a b Huttonは、失敗する可能性bindのあるタイプaと、失敗する可能性のあるマッピングabを指定すると、失敗する可能性のある結果bを生成するaを抽象化します。(ハットン、2016年)[7]
  4. ^ a b (Hutton 2016)は、Justは成功を示し、Nothingは失敗を示す可能性があると述べています。[7]
  5. ^ 意味的には、 Mは自明ではなく、すべての適切に入力された値カテゴリに対するエンドファンクターを表します。
  6. ^ プログラミング用語では(パラメトリック多形)関数ですが、単位(圏論ではηと呼ばれることが多い)は数学的に自然変換であり、関手間でマッピングされます
  7. 一方、 bind は、圏論における自然変換ではなく、拡張です。これは、マッピング(値から計算へ)を計算間の射に持ち上げます。
  8. ^ 厳密に言えば、バインドは、数学ではなくラムダ計算内のアプリケーションに対応するため、すべてのコンテキストで正式に厳密なラムダ計算では、バインドを評価するには、最初に右の項(2つのモナディック値をバインドする場合)またはバインド自体(2つのモナディック関数間)を無名関数でラップして、左からの入力を受け入れる必要があります。[10]
  9. ^ GHCバージョン7.10.1までに、そして今後、HaskellはHaskellの2014 Applicative Monad提案(AMP)の施行を開始しました。これは、モナドを使用する既存のモジュールに7行のコードを挿入する必要があります。[25]
  10. ^ Haskellのようないくつかの言語は、異なるパラメータ数の複数のバージョンとともに、と呼ばれる他のコンテキストでマップの仮名を提供します。詳細はここでは無視されます。lift
  11. ^ 圏論では、、その逆関数を持つ任意の関手の随伴Identityから出現していると見なすこともできます
  12. ^ 圏論は、これらのコレクションモナドを、セットカテゴリからモノイドのカテゴリまでのフリーファンクターとさまざまなファンクター
  13. ^ 代数的に、2つの(非可換)モノイドの側面間の関係は、半環に近いものに似ており、一部の加法モナドはそのように適格です。ただし、すべての加法モナドが半環に近い場合でも分配法則を満たすわけではありません。[33]
  14. ^ Haskellでは、 extendは実際には入力を入れ替えて定義されていますが、この記事ではカリー化は使用されていないため、ここではバインドの正確な双対として定義されています。

参考文献

  1. ^ a b c d e f g O'Sullivan、ブライアン; ゲルゼン、ジョン; スチュワート、ドン(2009)。「モナド」実世界のハスケルカリフォルニア州セバストポル:オライリーメディア。第14章ISBN 978-0596514983
  2. ^ a b ワドラー、フィリップ(1990年6月)。モナドを理解するLISPと関数型プログラミングに関するACM会議。ニース、フランス。CiteSeerX10.1.1.33.5381_ 
  3. ^ a b c Moggi、Eugenio(1991)。「計算とモナドの概念」(PDF)情報と計算93(1):55–92。CiteSeerX10.1.1.158.5275_ 土井10.1016 / 0890-5401(91)90052-4  
  4. ^ a b c d e Wadler、Philip(1992年1月)。関数型プログラミングの本質プログラミング言語の原理に関する第19回ACMシンポジウム。ニューメキシコ州アルバカーキ。CiteSeerX10.1.1.38.9516_ 
  5. ^ a b Hudak、Paul ; ピーターソン、ジョン; ファセル、ジョセフ(1999)。「モナドについて」Haskell98の穏やかな紹介第9章。
  6. ^ CA McCannの回答(2010年7月23日23:39)Haskell Contモナドはどのようにそしてなぜ機能するのですか?
  7. ^ a b c Graham Hutton(2016)Haskell 2ndEditionでのプログラミング
  8. ^ a b ベッカーマン、ブライアン(2012年11月21日)。「モナドを恐れないでください」YouTube{{cite web}}: CS1 maint: url-status (link)
  9. ^ Spivey、Mike(1990)。「例外の機能理論」(PDF)コンピュータプログラミングの科学14(1):25–42。土井10.1016 / 0167-6423(90)90056-J
  10. ^ 「モナド法」HaskellWikihaskell.org 2018年10月14日取得
  11. ^ 「モナドは何ではないか」2018年10月7日。
  12. ^ De Meuter、Wolfgang(1997)。AOPの理論的基盤としてのモナド(PDF)ECOOPでのアスペクト指向プログラミングに関する国際ワークショップ。ユヴァスキュラ、フィンランド。CiteSeerX10.1.1.25.8262_  
  13. ^ 「モナド(比喩なし)」HaskellWiki2009年11月1日2018年10月24日取得
  14. ^ オサリバン、ブライアン; ゲルゼン、ジョン; スチュワート、ドン(2009)。「Parsecの使用」実世界のハスケルカリフォルニア州セバストポル:オライリーメディア。第16章ISBN 978-0596514983
  15. ^ スチュワート、ドン(2007年5月17日)。「自分のウィンドウマネージャーを転がす:ジッパーでフォーカスを追跡する」Control.Monad.Writer2018年2月20日にオリジナルからアーカイブされました2018年11月19日取得
  16. ^ ベントン、ニック(2015)。「カテゴリーモナドとコンピュータープログラミング」(PDF)ロンドン数学会Impact150ストーリー1 2018年11月19日取得
  17. ^ Kiselyov、Olag(2007)。「オペレーティングシステムの区切られた継続」。コンテキストのモデリングと使用コンピュータサイエンスの講義ノート。スプリンガーベルリンハイデルベルク。4635291〜302ページ。土井10.1007 / 978-3-540-74255-5_22ISBN 978-3-540-74255-5
  18. ^ Meijer、Erik(2012年3月27日)。「あなたのマウスはデータベースです」ACMキュー10(3):20–33。土井10.1145 /2168796.21690762018年11月19日取得
  19. ^ Iverson、ケネス(1987年9月)。「APLの辞書」APLクォートクワッド18(1):5–40。土井10.1145 /36983.36984ISSN1088-6826_ S2CID18301178 _ 2018年11月19日取得  
  20. ^ クライスリ、ハインリッヒ(1965)。「すべての標準的な構造は、随伴関手のペアによって引き起こされます」(PDF)アメリカ数学会の議事録16(3):544–546。土井10.1090 / S0002-9939-1965-0177024-42018年11月19日取得
  21. ^ ピーターペッパー編 (1997年11月)。プログラミング言語オパール(テクニカルレポート)(第5版修正版)。Fachbereich Informatik、TechnischeUniversitätBerlin。CiteSeerX10.1.1.40.2748_ 
  22. ^ Moggi、Eugenio(1989年6月)。計算ラムダ計算とモナド(PDF)コンピュータサイエンスにおける論理に関する第4回年次シンポジウム。カリフォルニア州パシフィックグローブ。CiteSeerX10.1.1.26.2787_  
  23. ^ a b ペイトン・ジョーンズ、サイモンL .; ワドラー、フィリップ(1993年1月)。命令型関数型プログラミング(PDF)プログラミング言語の原理に関する第20回ACMシンポジウム。サウスカロライナ州チャールストン。CiteSeerX10.1.1.53.2504_  
  24. ^ Brent Yorgey Typeclassopedia
  25. ^ スタックオーバーフロー (2017年9月8日)haskellで新しいモナドを定義すると、Applicativeのインスタンスは発生しません
  26. ^ ブレントYorgeyモノイド
  27. ^ 「アプリケーションファンクター」HaskellWikiHaskell.org。2018年5月7日。2018年10月30日のオリジナルからアーカイブ2018年11月20日取得
  28. ^ a b Gibbard、Cale(2011年12月30日)。「コンテナとしてのモナド」HaskellWikiHaskell.org。2017年12月14日にオリジナルからアーカイブされました2018年11月20日取得
  29. ^ a b ピポニ、ダン(2006年8月7日)。「あなたはモナドを発明したかもしれません!(そして多分あなたはすでに持っています。)」無限の近所2018年10月24日にオリジナルからアーカイブされました2018年10月16日取得
  30. ^ 「F#計算式の詳細」2018年10月9日取得
  31. ^ 「表記は有害と見なされません」HaskellWiki 2018年10月12日取得
  32. ^ Giles、Brett(2013年8月12日)。「リフティング」HaskellWikiHaskell.org。2018年1月29日にオリジナルからアーカイブされました2018年11月25日取得
  33. ^ a b Rivas、Exequiel; ジャスケリオフ、マウロ; Schrijvers、Tom(2015年7月)。モノイドから半環に近いものまで:MonadPlusとAlternativeの本質(PDF)宣言型プログラミングの原則と実践に関する第17回国際ACMシンポジウム。イタリア、シエナ。CiteSeerX10.1.1.703.342_  
  34. ^ Swierstra、Wouter(2008)。「データ型アラカルト」(PDF)機能的な真珠。機能プログラミングジャーナルケンブリッジ大学出版局。18(4):423–436。CiteSeerX10.1.1.101.4131_ 土井10.1017 / s0956796808006758ISSN1469-7653_ S2CID21038598_    
  35. ^ キセリョフ、オレグ(2012年5月)。Schrijvers、トム; ティーマン、ピーター(編)。反復(PDF)機能および論理プログラミングに関する国際シンポジウム。コンピュータサイエンスの講義ノート。7294.神戸、日本:Springer-Verlag。pp。166–181。土井10.1007 / 978-3-642-29822-6_15ISBN  978-3-642-29822-6
  36. ^ Uustalu、Tarmo; ヴェネ、ヴァルモ(2005年7月)。Horváth、Zoltán(編)。データフロープログラミングの本質(PDF)最初のサマースクール、中央ヨーロッパの関数型プログラミング。コンピュータサイエンスの講義ノート。4164.ハンガリー、ブダペスト:Springer-Verlag。pp。135–167。CiteSeerX10.1.1.62.2047_ ISBN   978-3-540-46845-5
  37. ^ Uustalu、Tarmo; ヴェネ、ヴァルモ(2008年6月)。「計算のComonadic概念」理論的コンピュータサイエンスにおける電子ノートエルゼビア。203(5):263–284。土井10.1016 /j.entcs.2008.05.029ISSN1571-0661_ 
  38. ^ パワー、ジョン; 渡辺宏(2002年5月)。「モナドとコモナドの組み合わせ」(PDF)理論計算機科学エルゼビア。280(1–2):137–162。CiteSeerX10.1.1.35.4130_ 土井10.1016 / s0304-3975(01)00024-xISSN0304-3975_   
  39. ^ Gaboardi、Marco; 勝俣真也; オーチャード、ドミニク; ブリューバート、フラビエン; Uustalu、Tarmo(2016年9月)。グレーディングによる効果と共効果の組み合わせ(PDF)関数型プログラミングに関する第21回ACM国際会議。奈良、日本:コンピューティングマシナリー協会。pp。476–489。土井10.1145 /2951913.2951939ISBN  978-1-4503-4219-3

外部リンク

HaskellWikiの参照:

  • AllAboutMonads」(元々はJeff Newbernによる)—すべての一般的なモナドとそれらがHaskellでどのように機能するかについての包括的な議論。「機械化された組立ライン」のアナロジーが含まれています。
  • Typeclassopedia」(元々はBrent Yorgeyによる)—モナドを含むHaskellの主要な型クラスがどのように相互に関連しているかの詳細な説明。

チュートリアル:

興味深い事例: