JavaScriptのMaybeモナド
使ってみる
上記のモナドを使ってみます。サンプルは id:kazu-yamamoto さんのMaybe モナドの秘密 - あどけない話を参考にさせていただきました。
次のように名前と親を格納したデーターがあるとします。Bobの親はDave、その親はSteve、その親はTonyです。Tonyの親は不明 (undefined) です。
// 親子を表すデーター var tony = { name: "Tony", parent: undefined }; var steve = { name: "Steve", parent: tony }; var dave = { name: "Dave", parent: steve}; var bob = { name: "Bob", parent: dave };
ある人物を与えたときにその人のひいおじいさんを返すような関数は、途中でundefinedに当たることを考慮して、次のように書くことができます。
// ひいおじいさんを返す関数 (Maybeモナドを使っていない) function findGrandGrandFather(p) { if (p.parent) { if (p.parent.parent) { return p.parent.parent.parent; } } return undefined; }
findGrandGrandFather(bob); // --> tony findGrandGrandFather(steve); // --> undefined
これをMaybeモナドで書き直してみましょう。Maybeモナドを使うと失敗するかも知れない演算を合成することができます。
まず人物を与えると親を返す関数fatherを定義します。親が存在する場合には親を内包するMaybe.Just(親)を、親が存在しない場合にはMaybe.Noneを返します。
// 親を返す関数 (Maybeモナドで返す) function father(p) { return (p.parent) ? new Maybe.Just(p.parent) : new Maybe.None(); }
これを使うとひいおじいさんを返す関数は次のように書くことができます。
// ひいおじいさんを返す関数 (Maybeモナドを使用) function findGrandGrandFatherM(p) { return (new Maybe.Just(p)).bind(father).bind(father).bind(father); }
findGrandGrandFatherM(bob); // --> Just(tony) findGrandGrandFatherM(steve); // --> None
bindで計算を合成しています。関数fatherの値が途中でNoneになった場合、後続の演算はすべてNoneを返します。
Maybeモナドを使わない元のコードでは「ひいひいおじいさんを返す関数」にするにはifのネストを上げる必要がありますが、Maybeモナドを使うと「.bind(father)」を追加するだけで済みます。
なお、私が作ったMaybeモナドでは内包する値を取り出す関数は用意していません。結果のJust(人物)から人物を取り出すにはvalueフィールドを参照します。また、「if (a.value) ...」のように書くとvalueフィールドがあるかどうか判断することができるため、Noneと識別することができます。