と同じ本の、p.268~p.274あたりでつまずいたので、メモを残しておく。
コードはここにおいてある。
akimichi.github.io
モナドの機能は、「値にコンテキストを付加すること」、「コンテキストを付加したまま処理を合成すること」である。
unit関数は、モナドのインスタンスを生成するための関数で、unit:: T => M[T]という型情報を持つ。
Tはモナドに渡される値の型であり、Mはモナドの型である。
flatMap関数は、処理を合成するための関数で、flatMap:: M[T] => FUN[T => M[S]] => M[S]という型情報を持つ。
M[T]というモナドのインスタンスを受け取ると、そのモナドから値を取り出してT => M[S]という関数を適用し、その結果としてM[S]を返す。
今回メモするモナドは、恒等モナドである。
恒等モナドは、値にコンテキストを付加することなく、そのまま値として扱う。
恒等モナドの関数を、以下のようにIDという名前空間に定義する。
const ID = { unit: (value) => { return value; }, flatMap: (instanceM) => { return (transform) => { return transform(instanceM); }; }, };
また、次のような関数を定義する。
const succ = (n) => { return n + 1; }; const double = (m) => { return m * 2; };
unit関数は、単に値を返しているだけである。
ID.unit(1) == 1;
flatMap関数を介して1にsucc関数を適用する処理は、succ(1)と同じ結果になる。
ID.flatMap(ID.unit(1))((one) => { return ID.unit(succ(one)); }) == succ(1)
これを順を追ってみていく。
まず、ID.flatMapは、引数ID.unit(1) = 1なので、以下を返す。
(transform) => { return transform(1); };
そして、(one) => { return ID.unit(succ(one)); }が適用される。
(one) => { return ID.unit(succ(one)); }(1)
よって元のモナドは、succ(1)と同じ結果になる。
次に、flatMap関数を入れ子にしたものをみていく。
これは、次の関数を合成する関数をおなじ働きをする。
const compose = (f, g) => { return (arg) => { return f(g(arg)); }; };
つまり、以下の等式が成り立つ。
ID.flatMap(ID.unit(1))((one) => { return ID.flatMap(ID.unit(succ(one)))((two) => { return ID.unit(double(two)) }) }) == compose(double, succ)(1)
これを順を追ってみていく。
まず、以下の関数に着目する。
ID.flatMap(ID.unit(succ(one)))((two) => { return ID.unit(double(two)) })
ID.flatMapは以下を返す。
(transform) => { return transform(ID.unit(succ(one))); };
そして、(two) => { return ID.unit(double(two)) }が適用される。
((two) => { return ID.unit(double(two)) })(ID.unit(succ(one)))
よって、元の以下の関数は、
ID.flatMap(ID.unit(1))((one) => { return ID.flatMap(ID.unit(succ(one)))((two) => { return ID.unit(double(two)) }) })
以下のようになる。
ID.flatMap(ID.unit(1))((one) => { return ((two) => { return ID.unit(double(two)) })(ID.unit(succ(one))) })
ID.flatMapは、引数ID.unit(1) = 1なので、以下を返す。
(transform) => { return transform(1); };
よって、以下の関数が適用されると、
(one) => { return ((two) => { return ID.unit(double(two)) })(ID.unit(succ(one))) }
最終的には以下のようになる。
(one) => { return ((two) => { return ID.unit(double(two)) })(ID.unit(succ(one))) }(1)
よって元のモナドは、関数を合成する関数と同じ働きをする。
const compose = (f, g) => { return (arg) => { return f(g(arg)) } } ID.flatMap(ID.unit(1))((one) => { return ID.flatMap(ID.unit(succ(one)))((two) => { return ID.unit(double(two)) }) }) == compose(double, succ)(1)