モナド入門


Pueden leer la versión en español .


ああ悪名高い単語.JavaScriptでは話さないもの.さて、今日、我々はそれについて話をしています、特に、我々が本当に好きである1つの定義をレビューするつもりです.我々の正気を保つために、我々はちょうど我々がJavaScriptを使ってモデル化することができる局面を調査するつもりです.準備万端?始めましょう.
はい.これは簡単なもの、私誓う.モナドは…

pointed functors that can flatten.


あなたは、あなたが準備ができていると言いました.とにかく、我々はこれを行うことができます.一旦機能家の行動を理解するならば、残りは場所に落ちます.

ファンクション


JavaScriptの観点から、非常に特殊な機能を持つコンテナとして、それらを考えることができます.それらは、コンテナを離れることなくフィットするように、任意の方法で内部の値を変換することができます.
その魅力的ではないか.コードでどのように見えるでしょう.我々が考えることができる最も簡単なファンターを作ろうとしましょう.


function Box(data) {
  return {
    map(fn) {
      return Box(fn(data));
    }
  }
}
何が起こるのですか.さて、我々はBox 特に保持するように設計されたdata 値と値へのアクセスを得る唯一の方法はmap メソッド.このmap 物が機能するfn 引数として、data そして結果を別のものに戻すBox . 私は、すべての機能家がこれのように見えるというわけではないとあなたに話さなければなりません、しかし、一般に、これは彼ら全員が続くパターンです.それを使いましょう.
const xbox = Box('x');
const to_uppercase = (str) => str.toUpperCase();

xbox.map(to_uppercase).map(console.log);
// => X
// => Object { map: map() }
それでBox であるようです.役に立たない.ええ、それは私のデザインではなく、実際にはIdentity ファンター.それは私たちの日の日のコーディングで有用ではないが、教育目的のために魅力のように動作します.
これらの関数の利点は何ですか?抽象化のこの小さい層を加えることによって、我々は純粋な計算から「影響」を切り離すことができます.これを説明するために、実際の目的で1つのファンターを見てみましょう.

馴染みの顔


これは既に知っているかもしれませんが、配列はBox . チェックアウト.
const xbox = ['x'];
const to_uppercase = (str) => str.toUpperCase();

xbox.map(to_uppercase);
// => Array [ "X" ]
配列はコンテナで、map 内部で保持している値を変換し、変換された値が再び新しい配列でラップされるメソッドです.
OK、それはすばらしいです、しかし、配列の「影響」は何ですか?彼らはあなたに1つの構造の中に複数の値を保持する能力を与えます.Array.map 特に、コールバック関数が配列内のすべての値に適用されるようにします.あなたの配列の中に100個の項目を持っていたり、何もない場合は問題ありません..map あなたが値をどうするかについて集中することができるように、それがコールバック機能を適用するべきであるときに対処するロジックの世話をします.
もちろん、エラーハンドラやnullチェックのように、ファンクションを使用することもできます.現在、私はこれについて話し続けたいです、しかし、我々はmonad定義に戻る必要があります.

尖った部分


ですから、私たちのファンは「指摘」する必要があります.これは、我々の関数の最も単純な単位にどんな値も置くことができるヘルパー機能を必要とすると私たちに言っている空想的な方法です.この関数は“pure”と呼ばれ、他の名前は“unit”と“of”を含みます.
もう1度配列を見ましょう.配列の最も単純な単位に値を置くならば、我々は何を得ますか?はい、1つだけの項目を持つ配列.興味深いことに十分な組み込み関数が組み込まれています.
Array.of('No way');
// => Array [ "No way" ]

Array.of(42);
// => Array [ 42 ]

Array.of(null);
// => Array [ null ]
このヘルパー関数は、通常の関数の作成方法が多少畳み込まれている場合に特に便利です.この関数を使用すると、必要な値をラップして起動できます.map すぐにピン.まあそれにはもっと多くのことがありますが、それは主な考えです.続けましょう.

平地に


現在、我々は問題の核心に入りました.…を待つ.何が正確に問題ですか?
この状況を想像してくださいBox そして、我々はmap 関数を適用するaction . このようなこと.
const number = Box(41);
const action = (number) => Box(number + 1);

const result = number.map(action);
あなたがわかるまで、すべては微かに見えますaction 他を返すBox . So result 実際にはBox インサイド・アナザーBox : Box(Box(42)) . そして今、新しい値を取得するには、これを行う必要があります.
result.map((box) => box.map((value) => {/* Do stuff */}));
それは悪い.誰もそのようなデータで働きたくない.これは、モナドが我々を助けることができるところです.彼らは、これらの不必要な入れ子になった層を合併する「能力」を持っている機能家です.我々の場合にはBox(Box(42)) into Box(42) . どうやって?メソッドの助けを借りてjoin .
これが我々のように見えるBox .
  function Box(data) {
    return {
      map(fn) {
        return Box(fn(data));
      },
+     join() {
+       return data;
+     }
    }
  }
私はあなたが何を考えているか知っています.あなたは、私が「抽出」に名前を変えることを示唆するかもしれません.ちょっとそこにそれを持ちなさい.我々のところに戻りましょうaction 例として修正します.
const result = number.map(action).join();
田大!今、我々は得るBox(42) , 我々は、我々がちょうど1で欲しい値を得ることができますmap . ああ来て、あなたはまだ私に見て与えている?わかりました、名前を変えてみましょうextract , 現在、それはこれのようです.
const result = number.map(action).extract();
ここで問題は、もし私がそのラインだけで私は期待するresult 「普通の」価値であるために、私が自由に使うことができる何か.私は、私がAに対処しなければならないとわかるとき、少し動揺しますBox 代わりに.一方、私が読んだらjoin , 私は知っているresult それはまだモナドです、そして、私はそれの準備をすることができます.
JavaScriptを書いているだけで、これらの関数を無視して、monadsを必要としません.完全に、あなたはそれを行うことができます.悪いニュースは、関数の配列ですので、それらをエスケープすることはできません.良いニュースは、配列がmonadsであるので、入れ子構造のこの状況に入るとき、そして、あなたは簡単にそれを修正することができます.
したがって、配列にはjoin メソッド.私は、彼らがすることを意味します、しかし、それは呼ばれますflat . 見よ.
[[41], [42]].flat();
// => Array [ 41, 42 ]
そこへ行くとflat あなたの方法で取得余分な層について心配せずに移動することができます.それは実際には、モナドの本質と解決する問題です.
私が行く前に、私はもう一つのものをカバーする必要があります.

チェーンのモナド


この組み合わせはmap/join 実際には、これらの2つの機能を組み合わせたメソッドが一般的です.この1つはワイルドで複数の名前を持ちます:「チェーン」、「flatmap」、「bind」、「>>=」.特定の配列flatMap .
const split = str => str.split('/');

['some/stuff', 'another/thing'].flatMap(split);
// => Array(4) [ "some", "stuff", "another", "thing" ]
どのようにクールですか?代わりに2つのネストされた配列を持つ配列を持っています.これはネストした構造よりも扱いやすい.
しかし、それはあなたにいくつかのキーストロークを保存するだけでなく、同じ方法で機能の組成を奨励するかmap です.あなたはこのようなことができます.
monad.flatMap(action)
  .map(another)
  .map(cool)
  .flatMap(getItNow);
私はあなたが配列でこれをするべきでないと言っていません.あなた自身のモナドを作るならば、あなたはこのスタイルで機能を構成することができると言います.ただ、関数が必要なモナドを返すならflatMap , 使用しないmap .

結論


我々は、モナドが余分な機能を持つだけの機能家であることを学んだ.言い換えれば、彼らは魔法のコンテナです.他の容器を中に持つのを好みません?もう一度試してみましょう:彼らは魔法のタマネギです.ネロは、魔法のようにしましょう.
彼らはどんな効果にも“効果”を追加するために使用することができます.それで、エラー処理、非同期操作、副作用を扱うこと、および他のものの全体の束のようなもののためにそれらを使用できます.
私たちはまた、あなたがそれらを愛しているか、それらを憎むか、何の間にもないことを知りました.

ソース

  • Professor Frisby's Mostly Adequate Guide to Functional Programming. Chapter 9: Monadic Onions
  • Funcadelic.js
  • Fantasy Land
  • お読みありがとうございます.あなたがこの記事を役に立つならば、私の努力を支持したいです.buy me a coffee ☕ .