A word on Haskell Monads and C++
After spending a good while trying to understand monads in Haskell, and why the Haskell world is so fascinated by them, I finally understand why they aren’t as exciting to other languages, or why they are completely missing from languages like C++: because they’re mostly already there.
At its simplest, a monad is an abstraction of a value which knows how to apply functions to that value, returning a new monad. In other words, it’s a way to turn values into little packages that wrap additional functionality around that value. Sounds a lot like what an object does…
But this doesn’t tell you what’s exciting about them, from Haskell’s point of view. Another way of looking at them, without going into the wheres and whys, is this: In a lazily-evaluated, expression-based language, monads let you express sequenced, interdependent computation.
Consider the following two code examples. First, in C++:
#include
int main() {
std::cout << "Hello, world!"
<< " This is a sample"
<< " of using a monad in C++!"
<< std::endl;
return 0;
}
And the same code in Haskell:
module Main where
main :: IO ()
main = do putStr "Hello, world!"
putStr " This is a sample"
putStr " of using a monad in C++!"
putStr "\n"
What the IO monad in the second example is doing is making the sequenced evaluation of the print statements possible using a nice, normal looking syntax. The C++ code doesn't need monads to do this, because it already embodies the concept of abstracted values (here, the iostream passed between insertion operators) and sequenced computation (because it's not lazy).
To compare Monads with C++:
Monads are abstractions of values. So are most C++ objects.
Monads permit functions to be applied to the “contained” value, returning a a new version of the monad. C++ objects provide methods, where the mutated object is the new version.
Monads provide a way to encapsulate values in new monads. C++ objects have constructors.
As another example, consider the case where you have to call five functions on an integer, each using the return value of the last:
j(i(h(g(f(10))))
This is an identical operation in both Haskell and C++. But what if the return value of each function wasn't an integer, but an “object” that could either be an integer, or an uninitialized value? In most languages, there's either a type, or syntax, for this concept:
C++ boost::optional
C# int?
Java Integer
Haskell Maybe Int
If each function returns one of these, but takes a real integer, it means we have to check the “null” status of each return value before calling the next function. In C++ this leads to a fairly common idiom:
if (boost::optional x1 = f(10))
if (boost::optional x2 = g(*x1))
if (boost::optional x3 = h(*x2))
if (boost::optional x4 = i(*x3))
j(*x4);
Note that not only are these calls sequential, but due to the meaning of optionality, they are also inherently short-circuiting. If f returns none, none of the other functions get called.
Haskell can do this type of thing natively as well, and it looks similar:
case f 10 of
Nothing -> Nothing
Just x1 ->
case g x1 of
Nothing -> Nothing
Just x2 ->
case h x2 of
Nothing -> Nothing
Just x3 ->
case i x3 of
Nothing -> Nothing
Just x4 -> j x4
But it’s ugly as sin. In C++, we can be evil and flatten things out using basic features of the language, assuming we pre-declare the variables:
( (x1 = f(10))
&& (x2 = g(*x1))
&& (x3 = h(*x2))
&& (x4 = i(*x3))
&& (x5 = j(*x4)), x5)
Or you can eliminate the use of temporaries altogether by creating a wrapper class:
template struct Maybe {
boost::optional value;
Maybe() {}
Maybe(const T& t) : value(t) {}
Maybe(const Maybe& m) : value(m.value) {}
Maybe operator>>(boost::function,Maybe(const T&)> f) const {
return value ? f(*value) : *this;
}
};
If we change our functions to return Maybe instead of just boost::optional, it allows us to write this:
f(10) >> g >> h >> i >> j
Which in Haskell is written almost the same way:
f 10 >>= g >>= h >>= i >>= j
But where Haskell needs Monads to make this type of thing reasonable and concise, C++ doesn’t. We get passing around of object state between function calls as part of the core language, and there are many different ways to express it. However, if you confined C++ to function definitions and return statements only — where all function arguments were pass-by-value — then things like Monads would become an essential technique for passing knowledge between calls.
So it’s not that you can’t use Monads in C++, it’s just that they require enough extra machinery, and aren’t unique enough compared to core features of the language, that there isn’t the same level of motivation for them as there is in Haskell, where they can really add to the expressiveness of code.






I think you have a comma where you need a “>” in your
operator>>, and you’re missing a template argument list.Too bad about the associativity of
operator>>=, isn’t it?I wouldn’t say that C++ doesn’t need monads to make that sort of thing concise… especially not if, as you imply in your first code example, you consider
operator<<(std::ostream&, std::string const&)to be a monad. But of course that first example is mutating. To get non-mutating semantics with monad syntax in C++ you had to do something just about as messy as what you have to do in Haskell.Here‘s my approximation of a C++ maybe monad; what do you think?
Hmm, reading up a bit at I see I got it wrong. Now fixed. Still interested to know why bind has to be
(>>=) :: M a -> ( a -> M b ) -> M binstead of
(>>=) :: M a -> ( a -> b ) -> M bwhich would seem to be more convenient and just as flexible.
Sometimes the monad object doesn’t “contain” the value, but produces it on demand (such as by reading from an input stream at the time when the value is actually needed, lazily). If the job of >>= were to wrap the return value of the bound functor for you, then it would always be a value-contained type monad. By allowing the functor to return another instance of the monad, which is then just returned by >>=, then the magicness of any given monad is never stripped away for the sake of simplifying the bound functor.
Of course, for value-containing monads, you could always write a function that takes a -> b, and returns a -> M b, by simplying applying “return” to the value returned from a -> b.
`M a -> ( a -> b ) -> M b` can be implemented for _any_ monad:
f a b = do
a’ (a -> b) -> M b` does not strip away the _magicness_ of a monad — you still can’t tell Haskell, ‘given `M a`, give me the `a` it contains’. All you can tell is ‘given `M a`, please apply this function to the value _inside_ `M a`’. It is true `M a` may not always “contain a value”, but above description is an intuitive way to understand what happens, I think.
Oops, code formatting issue, I meant
f a b = do
a' <- a
return (b a')
M a -> (a -> b) -> M bcan be implemented for any monad:f a b = a >>= (return . b)(or using a betterdonotation that I’m unable to format correctly). It does not strip away the magicness of a monad — you still can’t tell Haskell, “givenM a, give me theait contains”. All you can tell is “givenM a, please apply this function to the value insideM a“. It is true thatM amay not always “contain a value”, but above description is an intuitive way to understand what happens, I think.Also, can you please the two other similar posts I made on this thread? Couldn’t get the formatting right in them.
I mean, can you please delete the two other similar posts …
`M a -> ( a -> b ) -> M b` is basically `flip liftM`.
In fact, M does not even need to be a Monad, it can be a Functor (all Functors are monads, though not automatically so because of historical cruft), in which case the expression is `flip fmap`.
I don’t think your explanation explains monads in general, just some specific use cases (State I suppose, and Maybe, though you don’t treat both in the same way). How would you model the List monad (non-determinism), or the continuation monad, in your setting?
Monads are far, far more powerful than what you show here. You discuss printing (IO) and maybe, but the real power of monads come from using (and combining) the state, continuation, list and eventual monads. I really do not think your post scales beyond the simplistic examples of monads you have shown, but am curious to see it in action.
Also, use the do notation please: ignoring it is not healthy when discussing monads…
You can clean up your using the Maybe monad, or with a MaybeT monad transformer.
You can get rid the case statements that way.
I think you’re sort of missing the whole point. Haskell allows you to write
do
x <- thisMayReturnMaybe 10
y <- thisMayReturnMaybe x
if y < 10 then Nothing else (Just y)
Note how the do notation allows you to pretend that you're assigning to x and y while you're really doing something (arbitrarily) more complex.
This is where the monadic framework really kicks in — you go about doing your business and the actual error handling / state threading is taken care of behind the scenes.