shand/maybe.hpp
#ifndef SHAND_MAYBE_INCLUDE #define SHAND_MAYBE_INCLUDE #include <boost/variant.hpp> #include <boost/utility/result_of.hpp> #include <boost/bind.hpp> #include <algorithm> #include <utility> #include <vector> #include <ostream> namespace shand { class nothing {}; template <class T> class just { public: T val; just(const T& val) : val_(val) {} }; template <class T> struct maybe { typedef boost::variant<nothing, just<T> > type; }; template <class T> inline typename maybe<T>::type monad_return(const T& x) { return just<T>(x); } template <class T, class First, class Second> inline typename maybe<Second>::type lookup(const T& a, const std::vector<std::pair<First, Second> >& v) { typedef std::vector<std::pair<First, Second> >::const_iterator iterator; iterator it = std::find_if(v.begin(), v.end(), boost::bind(&std::pair<First, Second>::first, _1) == a); if (it == v.end()) { return nothing(); } else { return just<Second>(it->second); } } template <class T> class lookup_functor { T a_; public: lookup_functor(const T& a) : a_(a) {} template <class Signature> struct argument_of; template <class R, class Arg> struct argument_of<R(Arg)> { typedef Arg type; }; template <class Signature> struct second_type; template <class First, class Second> struct second_type<std::vector<std::pair<First, Second> > > { typedef Second type; }; template <class Signature> struct result { typedef typename maybe<typename second_type<typename argument_of<Signature>::type>::type>::type type; }; template <class First, class Second> typename maybe<Second>::type operator()(const std::vector<std::pair<First, Second> >& v) const { return lookup(a_, v); } }; template <class T> inline lookup_functor<T> lookup(const T& a) { return lookup_functor<T>(a); } template <class T, class F> inline typename boost::result_of<F(T)>::type operator>>=(const boost::variant<nothing, just<T> >& x, F f) { if (boost::get<nothing>(&x) == 0) { return f(boost::get<just<T> >(x).val); } else { return nothing(); } } template <class Elem, class Traits, class T> inline std::basic_ostream<Elem, Traits>& operator<<(std::basic_ostream<Elem, Traits>& os, const boost::variant<nothing, just<T> >& x) { if (const just<T> *p = boost::get<just<T> >(&x)) { os << p->val; } return os; } } // namespace shand #endif // SHAND_MAYBE_INCLUDE
#include <iostream> #include <vector> #include <string> #include <utility> #include <shand/maybe.hpp> using namespace std; using namespace shand; vector<pair<string, string> > make(const std::pair<string, string>& p1) { vector<pair<string, string> > tmp; tmp.push_back(p1); return tmp; } vector<pair<string, string> > make(const std::pair<string, string>& p1, const std::pair<string, string>& p2) { vector<pair<string, string> > tmp; tmp.reserve(2); tmp.push_back(p1); tmp.push_back(p2); return tmp; } vector<pair<string, vector<pair<string, string> > > > config() { vector<pair<string, vector<pair<string, string> > > > v; v.reserve(3); v.push_back(make_pair("database", make(make_pair("path", "var/app/db"), make_pair("encoding", "euc-jp")))); v.push_back(make_pair("urlmapper", make(make_pair("cgiurl", "/app"), make_pair("rewrite", "True")))); v.push_back(make_pair("template", make(make_pair("path", "/var/app/template")))); return v; } int main() { cout << ((monad_return(config()) >>= lookup(string("database"))) >>= lookup(string("encoding"))) << endl; }
euc-jp
C++0xだとこう
#include <iostream> #include <vector> #include <string> #include <utility> #include <shand/maybe.hpp> using namespace std; using namespace shand; int main() { vector<pair<string, vector<pair<string, string>>>> config = { {"database", {{"path", "var/app/db"}, {"encoding", "euc-jp"}}}, {"urlmapper", {{"cgiurl", "/app"}, {"rewrite", "True"}}}, {"template", {{"path", "/var/app/template"}}} }; cout << ((monad_return(config) >>= lookup(string("database"))) >>= lookup(string("encoding"))) << endl; }
ちなみに、Haskellだとこう
main = print $ return config >>= lookup "database" >>= lookup "encoding" config :: [(String, [(String, String)])]; config = [ ("database", [("path", "var/app/db"), ("encoding", "euc-jp")]), ("urlmapper", [("cgiurl", "/app"), ("rewrite", "True")]), ("template", [("path", "/var/app/template")]) ]
おまけ
#include <iostream> #include <shand/maybe.hpp> using namespace std; using namespace shand; maybe<int>::type inc(int x) { if (x >= numeric_limits<int>::max()) { return nothing(); } return monad_return(x+1); } int main() { int start = numeric_limits<int>::max() - 2; cout << (monad_return(start) >>= inc) << endl; cout << ((monad_return(start) >>= inc) >>= inc) << endl; cout << (((monad_return(start) >>= inc) >>= inc) >>= inc) << endl; // nothingなので何も表示されない cout << ((((monad_return(start) >>= inc) >>= inc) >>= inc) >>= inc) << endl; // 最後のincは呼ばれない }
Maybeモナドを表現するのにboost::variant
HaskellのNothingとJustにこだわらないならboost::optionalを使ったほうがいいと思うんだ。
Initializer listsとCallableコンセプトとラムダ式とTemplate Aliasesがあればもっと簡単に書けるんだけど・・・
早くC++0xを使いたいな。