Panda Noir

JavaScript の限界を究めるブログです。

モナドってすごく簡単だ!

関数型プログラミングしたりHaskellしてるとたびたび遭遇する「モナド」。実は大学1年の僕も理解できるくらい簡単です。この記事ではモナドとは何なのか解説したいと思います。

(注: モナドモナドでもプログラミングでのモナドについての記事です。圏論モナドとはやや異なります)

モナドとは

モナドとは、モナド則を満たすものの総称です。

モナド則と言われても何のこっちゃですよね。例えば、「ニャンコ則とニャンコ」について考えます。

「『かわいくてニャーと鳴く』というニャンコ則を満たすものをニャンコと呼ぶ」

こうニャンコ則とニャンコを定義すると三毛猫もペルシャ猫もニャンコです。しかし、ニャーニャー鳴くオタクは可愛くないのでニャンコではありません。

モナド則とモナドの関係もちょうどこれと同じです。

抽象化とモナド

先のニャンコ則の例で言うと、三毛猫もペルシャ猫も「ニャンコ」です。つまり「ニャンコ」という「抽象」を「具体」に落とすと三毛猫やペルシャ猫になります。逆に、三毛猫やペルシャ猫を抽象化するとニャンコになります。

(注意: 二つを抽象化するとニャンコにもなる、というだけで二つを抽象化したもの自体は動物や猫など様々あります)

「ニャンコ」は存在しない

ここまでで分かる通り、「ニャンコ」という猫は存在しません。ニャンコ「である」猫は存在します。

そしてモナドについてもニャンコと同じことが言えます。

MaybeやListはモナド則を満たすのでモナドです。しかし、モナド」というモナドは存在しません

モナド則とは

さて、これで「モナド則を満たすものはモナド」ということまでは分かりましたね。では、モナド則とは具体的にどのような則なのでしょうか?

モナド則とは以下のような則を指します(javascriptで書かれていますが、基本的な考え方はどの言語でも共通です)

Monad.of(x).chain(f) === f(x);
Monad.of(x).chain(identity) === Monad.of(x);
(Monad.of(x).chain(f)).chain(g) === Monad.of(x).chain((x) => f(x).chain(g));

未定義のものが多くてよくわかりませんね。順に説明するのでご安心を。

まず上のコードのof、chain、identityを定義します。

of

ofはモナドインスタンスを作ります。例えば配列(List)もモナドと言いました。ということは配列(Array)もofを持つことができます。というかES2015で提案されたArray.ofがまさにモナドのofです。

function MonadArray(x) {
  this.value = [x];
}
MonadArray.of = (x) {
  return new MonadArray(x);
}

つまり MonadArray.of(x) == [x] です([x]と書いてますが、JavaScriptの配列ではありません)。

chain

chain()はモナドが持つデータに対して渡された関数を利用して何らかの計算、処理を行うメソッドです。

配列はモナドなので、chain()も持っています。ただし、名前がchainではなくconcatMapです。chainをconcatMapで置き換えると上記のモナド則を満たすことができます。

concatMapの実装は以下のコードです。

MonadArray.prototype.concatMap = function(f) {
  function concat(list) {
    if (list.length === 0) return new MonadArray();
    return list.
  }
  return concat(this.value.map(f));
};
MonadArray.of(x).concatMap(f) === f(x);

MonadArray.of(x).concatMap(identity) === MonadArray.of(x);

(MonadArray.of(x).concatMap(f)).concatMap(g) === MonadArray.of(x).concatMap((x) => f(x).concatMap(g));

他のモナドもchainという名前ではないかもしれませんが、chainと同等のメソッドを1つ以上持っています。

identity

identityは恒等関数です。恒等関数とは数学の用語でf(x)=xのことです。

つまりこれです

function identity(x) {
  return x;
}

これだけです。

モナド則」を満たすという意味

これでモナドが何者なのかはわかりましたよね。え?わからない?モナド則って結局何者なのって?

モナド則というのはつまりのところ、「ある値に文脈を持たせる」働きをします。

文脈というとわかりづらいなら意味をつけるでもいいです。

あるxを配列に入れるのは「配列という意味をxに持たせる」と言えます。x自体に変更はしていませんよね?xに配列内のxという意味をつけたわけです。

xを配列に入れることになんの意味があるんだという人はいませんよね?

モナドは様々な、本当に様々な、意味を値に持たせます。モナドを活用するというよりはモナドという考え方を知っておくことに意味があるのでしょう。