From OnMonads, it seems that a better example is needed to help imperative programmers understand. Here is an attempt at a CeePlusPlus immitation of IO monads. I will explain each piece of code immediately above where it is displayed.

First, we need to include the C++ *iostreams* library so we can actually do some IO later.

Next is the definition of the#include <iostream>

Luckily, C++ allows us to override thetemplate<typename A_> struct monad{

Thetemplate<typename B_> monad<B_>& operator >>=(monad<B_>& (*f)(A_)){return *new monad_bind<A_,B_>(*this,f);}

Thisstatic monad<A_>& return_value(A_ a){return *new monad_return<A_>(a);}

Here is thevirtual A_ execute() = 0; // this should be protected, but that causes a c++ problemprivate:

The constructor takes its first argument as the monad previous to this one in the chain - the one it is bound to. The second argument is the side-effectful function that you would like to have called on the result of the previous monad.template<typename A_,typename B_> struct monad_bind : public monad<B_>{

These are the data members we use to hold the constructor's arguments until we are ready to use them inmonad_bind(monad<A_>& ma,monad<B_>& (*f)(A_)) : _ma(ma), _f(f){}

Here, you can see that the imperative implementation is straightforward. We just execute the previous monadmonad<A_>& _ma;monad<B_>& (*_f)(A_);

Here is thevirtual B_ execute(){A_ a=_ma.execute();monad<B_>& mb=_f(a);return mb.execute();}};

We use this specialtemplate<typename A_> struct monad_return : public monad<A_>{monad_return(A_ a) : _a(a) {}A_ _a;virtual A_ execute(){return _a;}};};

This function performs a side-effect. Here we draw another line between what the "application" can access, and what the "runtime" can access. For simplicity, again, there is no protection to enforce that division. The application should never call this function - it should only refer to it by name (function pointer). The application gives this function as a parameter to thestruct unit{};

Likemonad<unit>& write_character(char c){std::cout << c;return monad<unit>::return_value(unit());}

Here is a function that is part of our application. In this function, we receive a character as input, and test if it is capitalized. If it is a capitalized alpha character, we return "y", and otherwise we return "n".monad<char>& read_character(unit){char c;std::cin >> c;return monad<char>::return_value(c);}

Here is our "application". Here we just bind together some monads, and return the resultant monad. Note that this functionmonad<char>& is_it_capitalized(char c){returnisupper(c)?monad<char>::return_value('y'):monad<char>::return_value('n');}

Here is our "runtime". This function evaluates our "application", and captures the monad it returns. Next, it invokes all that behind-the-scenes stuff we talked about earlier.monad<unit>& program(monad<unit>& start){return (((start >>= read_character) >>= is_it_capitalized) >>= write_character);}

Whenvoid main(void){monad<unit>& result=program(monad<unit>::return_value(unit()));unit end=result.execute();}

This should give you some idea how monads are structured, but as you can imagine, a lot is lost in the language translation. In particular, since C++ doesn't have the concept of lambdas (anonymous inline functions), we have had to write *is_it_capitalized* as a separate function outside of *program*. That would quickly lead to a lot of clutter if we tried to write a non-trivial program this way. In languages where monads are more typically used, we could have written *program* similar to this:

There is typically also a specialprogram = read_character >>= is_it_capitalized >>= write_characterwhere is_it_capitalized c = if isupper cthen return 'y'else return 'n'

Maybe a version using the BoostLibraries? It would get rid of the monad_bind bits at least, which don't really contribute to the jist of the matter. On the other hand, boost is very functional, and maybe someone who was happy hoisting bind and lambda around wouldn't find monads that hard?

See OnMonads and also FunctoidsInCpp where much of the Haskell prelude has been implemented in C++.

EditText of this page (last edited April 29, 2007) or FindPage with title or text search