私は数学の用語やHaskellが理解する必要なくモナドの定義を定式化しようとしています。
モナドは、値を受け取り、それをラップして、機能的なスタイルで作業するときに役立つことが判明している特定のインターフェイスおよび動作の制約を満たす関数と考えることができますか?
インターフェイスの制約は次のとおりです。
私。型を定義する型コンストラクタがあります(これは何ですか?)。
ii。これには、値を対応するモナド型に変換する単位関数があります。
iii。これには、モナドを取り、いくつかの型を取り、モナドを返す関数(これは別のモナドですか?)であり、モナドを返す「バインド」メソッドがあります。
動作上の制約は次のとおりです。
私。左のアイデンティティ
Monad(x).bind(fn) == Monad(fn(x)); // for all x, fn
ii。正しいアイデンティティ
Monad(x).bind(function(x){return x;}) == Monad(x); // for all x
iii。関連性
Monad(x).bind(fn1).bind(fn2) == Monad(x).bind(function(x) {
return fn2(fn1(x));
});
これはモナドの最小限の定義ですか?
メイソンの答えは正しいです。そうです、私のシリーズを読んでください。彼のポイントをより徹底的に強調するには:
モナドは、値を受け取り、特定のインターフェースおよび動作の制約を満たすようにラップする関数と考えることができますか
はいですが、それはモナドを特徴付ける最善の方法ではありません。あなたは非常に近いですが;私たちはあなたの処方に小さな変更を加え、より良い特性化に到達することができます。
あなたはモナドと言って、それをM
と呼び、is値を受け入れ、ラップされた値を生成する関数です。問題があるのはisです。むしろ、モナドはそのような関数を持っています。モナドM
は、3つのもののコレクションです。
型を取り、新しい型を生成する変換。 C#では、このような変換を「ジェネリック型」と呼びます。ジェネリック型M<T>
、型int
、そして新しい型M<int>
を生成します。
タイプunit
の値を受け入れ、タイプM<T>
の値を生成する汎用関数T
。
タイプM<T>
の値とbind
からM<U>
までの関数を受け入れ、M<U>
を生成する汎用関数T
。
その2つ目は、「値を受け入れてラップする関数」です。モナドがその関数であることではありません。モナドは、モナド型コンストラクタ自体、ラッパー、およびモナドワークフローの最後に新しい関数をバインドする関数の組み合わせです。
これが、圏論でモナドが「トリプル」と呼ばれることがある理由です。モナドを特徴付けるために3つのものが必要だからです。
さて、あなたは私があなたと同じことをしていることに気付くでしょう。私の一連の記事では、モナドを特定の規則に従うジェネリック型として特徴付けています。つまり、モナドは最初のものであり、その最初のものには2番目と3番目のものがあるはずです。
パーツ間の関係をどの程度正確に特徴付けるかは、あまり重要ではありません。重要なのは、これらの3つが存在しなければならないということです。型を「増幅」する方法、値を「ラップ」する方法、およびモナドのインスタンスを、その上に関数を「バインド」して別のインスタンスに変換する方法です。これら3つのものがあり、それらがアイデンティティ、推移性などの明らかな規則に従っている場合、モナドがあります。
Eric Lippertは モナドに関する素晴らしいシリーズ を書いており、それは実際にはそれらを非ハスケラーにとって意味のある方法で説明しています。全体は読む価値がありますが、基本的な考え方は次のとおりです。
モナドは基本的な型を取り、それに新しい何かをする "型エンハンサー"です(型T
を_IEnumerable<T>
_のようなシーケンスに変換するなど):
モナドは次のようなジェネリック型_
M<T>
_です。
T
を取り、_M<T>
_を返すある種の構築メカニズムがあります。これは署名付きのメソッドとして特徴付けられています
static M<T> CreateSimpleM<T>(T t)
また、基になる型をその型のモナドに取る関数を適用する方法もあります。これは、署名付きのメソッドとして特徴付けられています。
static M<R> ApplySpecialFunction<A, R>(M<A> monad, Func<A, M<R>> function)
最後に、これらの両方の方法は、モナドの法則に従う必要があります。
- モナドの特定のインスタンスに構築関数を適用すると、モナドの論理的に同一のインスタンスが生成されます。
- 値の構築関数の結果に関数を適用し、その関数を値に直接適用すると、モナドの2つの論理的に同一のインスタンスが生成されます。
- 最初の関数に値を適用し、続いて結果に2番目の関数を適用し、最初の関数に2番目の関数の合成である3番目の関数を適用すると、論理的に同じモナドの2つのインスタンスが生成されます。
この定義が少しわかりにくいと思われる場合は、次の段落でその理由を説明します。
ふew!そして、おそらく今週、モナドの法則から始めるのではなく、例を見ることによってパターンを探究するという考えから、このシリーズを何週間も前に始めた理由がわかるでしょう。
最初の投稿 から始めてシリーズを読むと、コンセプトは実際には最後まで理解できるはずです。ありがたいことに、ここには「モナドはホットドッグのようなもの」という意味不明の言葉はありません。
i)タイプを取り、新しいタイプ、つまりジェネリックタイプを返す関数です。タイプがないため、jsで直接使用できるかどうかはわかりません。
iii)別のタイプを返しますが、同じモナド内にあります。つまり、m a-> m bから再投影できます。
これは最小限の定義ですが、通常、他の関数も定義されています。すべてのモナドはファンクターなので、モナドのfmapをいつでも定義できます。また、結合/フラット化関数を持っていることもよくあります(バインドはfmapと結合の観点から実装できます)。
モナドは、値を受け取り、それをラップして、関数型スタイルで作業するときに役立つことが判明している特定のインターフェイスおよび動作の制約を満たす関数と考えることができますか?
いいえ。値をラップする関数はユニットです。
モナドの法則に従うbindメソッドでオブジェクトを返す単位関数としてモナドを説明するのが理にかなっているかどうかを確認しようとしている場合、それはまったく機能しません。問題は、単位関数で作成できないモナド値があることです。
たとえば、Maybeモナドでは、Nothing値をunitで作成することはできません。Maybeモナドの存在の要点は、Nothing値を許可することです。
一般に、ユニット関数によって作成されたモナドの値は単純で退屈でプレーンなものであり、そもそもモナドを処理する必要がある理由を説明していません。 Unitは互換性のためにプレーン値をモナド値に、プレーン関数をモナド関数に変換するのに最適ですが、それだけです。
少し考えてみたところ、有用なすべてのモナドには単位では作成できないモナドの値があるという証拠を紙で実際に計算することができました。 (具体的には、モナドにモナド値が1つしかない(役に立たない)か、モナドが恒等モナドと同型である(恒等関数が役に立たないのと同じように役に立たない)か、モナドに次のモナド値があるユニットでは作成できません。)
これは間違っています
_Monad(x).bind(function(x){return x;}) == Monad(x); // for all x
_
そのはず
_unit(x) == function(x){return Monad(x);} // I'm assuming this is what you mean by Monad(x)
Mx.bind(unit) == Mx; // for all monadic values Mx
_
この
_Monad(x).bind(fn) == Monad(fn(x)); // for all x, fn
_
する必要があります
_Monad(x).bind(fn) == fn(x); // for all values x and monadic functions fn
_
バインドの実装が機能するのと同じ型のモナド値を返す1つの引数の関数である任意のモナド関数とバインドできますが、すべての関数とバインドすることはできません。
これは間違っています
_Monad(x).bind(fn1).bind(fn2) == Monad(x).bind(function(x) {
return fn2(fn1(x));
});
_
これは
_// for any monadic value Mx and any 2 monadic functions fn1 and fn2 whose types line up
Mx.bind(fn1).bind(fn2) == Mx.bind(function(x) {
return fn1(x).bind(fn2);
});
_
iii。これには、モナドを受け取る「バインド」メソッドがあり、あるタイプを取り、モナドを返す関数(これは別のモナドですか?)を返し、モナドを返します。
あなたはWordモナドをいくつかの異なる意味で使用していますが、それは問題ないと思いますが、まだモナドに頭を抱えていない人にとっては不明確かもしれません。モナドとは、3つの意味があります。モナド/モナドの一般的なパターン、特定のモナド、またはモナドの値です。一般にモナドはあなたが定義/説明しようとしているものであり、特定のモナドはこの一般的なパターン/インターフェースの特定のインスタンス(MaybeモナドやListモナドなど)であり、モナド値は次の型の値ですユニットまたはバインド出力(リストモナドのリスト_[1,2,3]
_など)。
それを単純化して
iii。これには、モナド値とモナド関数を取り、モナド値を返す「バインド」メソッドがあります。
ただし、異なるモナド間でバインドが機能しないことや、モナド関数の出力の型がバインドの出力の型と同じであることは明らかではありません。
Bindを確認するもう1つの方法は、モナド式の関数アプリケーションと同じです。プレーン関数アプリケーションは、プレーン値とプレーン関数を取り、プレーン値を返します。モナディックバインディングはモナディック値とモナディック関数を取り、モナディック値を返します。モナディック値Mx
とモナディック関数fn
がある場合、fn
は機能しないため、fn(Mx)
を実行できません。モナディック値。しかし、Mx.bind(fn)
を実行できます。
私。型を定義する型コンストラクタがあります(これは何ですか?)。
型コンストラクタはHaskellに存在する概念ですが、実際にはJavaScriptにはありません。これについてjavascriptで説明する最善の方法はわかりませんが、クラスについては実際には存在しませんが、javascriptプログラマーはその概念を知っているため、おそらくクラスについて説明することになります。どのモナドについても、その特定のモナドにとってモナドの値が何を意味するかの定義が必要であり、それがここにあります。