maybeモナドっぽいもの

Haskellに触れれば嫌でもモナドを意識しないわけにはいかない。だが、Haskellの仕様はモナドを表現するためにとても向いているとは言えるものの、モナド則を満たすように定義するのはユーザーの責任なんである。つまり、モナドというのはHaskellに与えられた言語機能というよりは、ある種のデザインパターンや規約のようなものなんだろう。
ならば他の言語でもモナドを表現することは可能なはずだ。というわけでC++でmaybeモナドっぽいものを書いてみた。それっぽい記述を優先したので意味的な正確さは気にしないでほしい。あくまでも余興ということで。それと、maybeを選んだのは比較的単純だから。

template <class a>
class monad {
private:
  a obj;
public:
  monad(a obj) :obj(obj) {}
  template <class b>
  b operator>>(b p(a)) {
    return p(obj);
  }
  a operator>>(a p(a)) {
    return obj;
  }
  template <class c, class b>
  c operator>>(b p) {
    return p(obj);
  }
};

template <class a>
monad<a> mreturn(a obj) {
  return monad<a>(obj);
}

template <class a>
class setter {
private:
  a& obj;
public:
  setter(a& obj) :obj(obj) {}
  monad<a> operator()(a& obj) {
    return monad<a>(this->obj=obj);
  }
};

class nothing {};

template <class a>
class maybe {
private:
  a obj;
  bool flag;
public:
  maybe(monad<a> m) :flag(false) {
    m.operator>><monad<a>,setter<a> >(setter<a>(obj));
  }
  maybe(nothing m) :flag(true) {}
  maybe() :flag(true) {}
  template<class f>
  maybe operator>>(f p) {
    return flag? nothing():p(obj);
  }
  template <class b>
  b operator>>(b p(a)) {
    return flag? b():p(obj);
  }
};

template <class a>
maybe<a> just(a o) {
  return maybe<a>(o);
}

ここまでが定義。で、以下の使い方を見るといかにもそれっぽいじゃないかね!

#include <iostream>
#include <map>
#include <string>
using namespace std;

class sheep {
public:
  string father, mother;
  sheep(string f="", string m="")
    :father(f), mother(m) {}
};

map<string, sheep> sheep_register;

maybe<string> father(string a) {
  return sheep_register[a].father=="" ?
    nothing() : just(sheep_register[a].father);
}

maybe<string> mother(string a) {
  return sheep_register[a].mother=="" ?
    nothing() : just(sheep_register[a].mother);
}

maybe<string> mothers_paternal_grandfather(string a) {
  // return mreturn(a) >>mother >>father;
  return mother(a) >>father;
}

int display(string a) {
  cout<<a;
  return 0;
}

int main(void) {
  sheep_register["mary"]=sheep("mary");
  sheep_register["adam"]=sheep();
  sheep_register["john"]=sheep("adam");
  sheep_register["mike"]=sheep("", "mary");
  sheep_register["beth"]=sheep("", "mary");
  sheep_register["suze"]=sheep("john","beth");
  sheep_register["don"] =sheep("mike","beth");
  sheep_register["duke"]=sheep("don","suze");
  mothers_paternal_grandfather("duke")>>display;

  return 0;
}

Document ID: 7bfbff742b14a3b8d9a9d808527aec16