遠くからのモナドのようなもの

「モナドの最も理解しやすい紹介」を読み、さまざまなフォーラムで(また)数十の議論を読んだ後、「モナドのようなもの」の解釈が正しいものに少し近づくのに役立つ抽象オブジェクト指向プログラマーのグループがあるという結論に達しました理解。



したがって、この出版物では、次の質問に対する答えは見つかりません。

1.モナドとは何ですか?

2.モナドの使用場所と方法

3.モナドが存在しないよりも優れているのはなぜですか?



プログラミングでは、このような現象があります-「デザインパターン」。 公式には、これは「典型的な問題」を解決する際に従うべき一連のベストプラクティスです。 非公式には、一般的な問題を解決するための組み込みツールを持たない言語のための松葉杖のセットにすぎません。



そのようなデザインパターンがあります- 通訳 。 まず第一に、それはあなたが好きなプログラミング言語の上にある種の仮想マシンを作ることができるので、驚くべきことです:



1.仮想マシンが理解できる言語でプログラムを記述できます。

2.仮想マシンが各命令をどのように解釈するかを記述するために、すべての詳細でコロバンを奪うことが可能です。



親切な読者が言及されたパターンに最低限精通している場合にのみ、以下に書かれているすべてが意味をなします。



比較的標準的な例:

function add(x) { return { op: "add", x: x }; } function div(x) { return { op: "div", x: x }; } function run(value, statements) { for(var i = 0; i < statements.length; ++i) { var statement = statements[i]; var op = statement.op; var x = statement.x; if(op === "add") { value += x; } else if(op === "div") { value /= x; } else { throw new Error("Unknown operation " + op); } } return value; } var program = [ add(10), div(3) ]; var result = run(0, program); console.log(result); // 3.3333...
      
      





GoFの愛好家は、「これは通訳ではなく、コマンドである」と主張することができます。 彼らにとっては、それをコマンドとしましょう。 この記事の文脈では、これはあまり重要ではありません。



この例では、まず、「10を加算」と「3で除算」という2つの命令で構成されるプログラムがあります。 それが何を意味するにせよ。 第二に、プログラムを見ながら何か意味のあることをするパフォーマーがいます。 「プログラム」は、その実行結果に非常に間接的に影響することに注意することが重要です。エグゼキューターは、命令を上から下に実行する義務はまったくなく、各命令を1回だけ実行する義務はありません。 () - 「ワールド」内



add()の console.log()への翻訳は私たちにとって面白くないことに同意します。 コンピューティングは面白いです。 したがって、不必要な柔軟性を放棄することでコードを少し単純化します。



 function add(x) { // add(2)(3) === 5 return function(a) { return a + x; }; } function div(x) { // div(10)(5) === 2 return function(a) { return a / x; }; } function run(value, statements) { for(var i = 0; i < statements.length; ++i) { var statement = statements[i]; value = statement(value); } return value; } var program = [ add(10), div(3) ]; var result = run(program); console.log(0, result); // 3.3333...
      
      





ここに滞在する価値があります。 プログラムを個別に記述し、「プログラムを実行する方法」を個別に記述できるツールがあります。 パフォーマンスの結果に対する希望に応じて、請負業者の実装は非常に異なる場合があります。



たとえば、 NaNnullまたはundefinedが計算のどこかに現れるとすぐに計算が停止し、結果がnullになるようにします



 ... function run(value, statements) { if(!value) { return null; } for(var i = 0; i < statements.length; ++i) { var statement = statements[i]; value = statement(value); if(!value) { return null; } } return value; } console.log(run(undefined, [add(1)])); // null console.log(run(1, [add(undefined)])); // null
      
      





いいね しかし、異なる初期値のコレクションに対して同じプログラムを実行したい場合はどうでしょうか? また質問ではありません:



 ... function run(values, statements) { return values.map(function(value) { for(var i = 0; i < statements.length; ++i) { var statement = statements[i]; value = statement(value); } return value; }); } var program = [ add(10), div(3) ]; console.log(run([0, 1, 2], program)); // [3.333..., 3.666..., 4]
      
      





ここに再び滞在する価値があります。 プログラムを説明するのに同じ表現を使用しますが、アーティストによって非常に異なる結果が得られます。 それでは、例をもう一度書き直してみましょう。 今回は、まず柔軟性を少し削除します。式は最初から最後まで厳密に実行されるようになり、次に、 run()内のループを取り除きます。 結果を単語コンテキストと呼びます(だれも推測しないように):



 ... function Context(value) { this.value = value; } Context.prototype.run = function(f) { var result = f(this.value); return new Context(result); }; var result = new Context(0) .run(add(10)) .run(div(3)) .value; console.log(result); // 3.3333...
      
      





実装は以前のオプションとは非常に異なりますが、ほぼ同じです。 ムンダダという用語を導入することが提案されています(英語のムーンアド -「月の広告」から)。 Hello Identity moonad:



 ... function IdentityMoonad(value) { this.value = value; } IdentityMoonad.prototype.bbind = function(f) { var result = f(this.value); return new IdentityMoonad(result); }; var result = new IdentityMoonad(0) .bbind(add(10)) .bbind(div(3)) .value; console.log(result); // 3.3333...
      
      





このことは、 Identityモナドに多少似ています。



次に、 NaNと戦ったアーティストのバージョンを思い出して、実装の新しいアプローチを使用して書き直そうとします。



 function MaybeMoonad(value) { this.value = value; } MaybeMoonad.prototype.bbind = function(f) { if(!this.value) { return this; } var result = f(this.value); return new MaybeMoonad(result); }; var result = new MaybeMoonad(0) .bbind(add(10)) .bbind(add(undefined)) .bbind(div(3)) .value; console.log(result); // null
      
      





さらに馴染みのある例:



 var person = { // address: { // city: { // name: "New York" // } // } }; console.log(person.address.city.name); //  console.log(new MaybeMoonad(person) .bbind(function(person) { return person.address; }) .bbind(function(address) { return address.city; }) .bbind(function(city) { return city.name; }) .bbind(function(cityName) { return cityName; }) .value); //  ,  null
      
      





遠くからは、 多分モナドのように見えるかもしれません。 親切な読者は、 リストモナドに似た何かを独自に実装するように招待されています。



基本的なファイルスキルでは、 f()呼び出しが非同期になるようにIdentityMoonadを変更することは意味がありません。 結果はPromise moonadqに似たもの)です。



さて、最新の例をよく見ると、多かれ少なかれmunadaの正式な定義を試みることができます。 Munadaは2つの操作を持つものです。

1. return-通常の値を取得し、それをムナディックコンテキストに入れて、このコンテキストを返します。 これは単なるコンストラクター呼び出しです。

2. bind-通常値を返す関数を通常値から取得し、それをムナディックコンテキストのコンテキストで実行し、モナドコンテキストを返します。 これは、「bbind()」の呼び出しです。



All Articles