People confuse special-cases (Monad instances) with Monads (the abstraction).
"Monads in C++" would be an encoding of the return/bind interface, which is possible (surprisingly, at least to me), and then writing a library of combinators that work with this interface and any instance of them.
What this article (and countless others) are about is a particular encoding of bind/return for a single type, and not the Monad abstraction in general.
The idea that C++ meta programming is most easily understood as an (unnecessary complicated) functional language actually makes a huge amount of sense to me, and I particularly enjoy the lovely sense of balance it invokes.
About fifteen seconds. Skimmed it, realized it was "another instance of someone attempting to do yet-another-bad-thing-in-C++", then closed it.
I went through the same phase in my C++ career. (Mine was attempting to add FOREACH to my projects without depending on Boost.) I think at some point every C++ dev gets tired of writing boilerplate code, and then spends the next few days implementing something that would've otherwise taken them a few hours.
Once you accept that C++ is not Lisp, and doesn't need to be, then you become much more productive.
Additionally, your resulting C++ code is much more likely to actually be used by other C++ engineers.
Lastly, these sorts of libraries tend to be very difficult to debug unless you were the one who wrote it.
It's in fact an attempt to explain how the author got to understand the inner workings of Boost's Proto library, which is already available for C++ and pretty much a successful project as far as it goes. For people also into Haskell, it's a very concise and insightful attempt indeed.
I understand your rant, but I'm not sure this is the place to go and spew it... Actually some people enjoy studying programming languages themselves and sometimes come up with useful stuff for engineers that like building more concrete stuff.
When I see someone towards the middle of their career who is still in the thrall of C++ metaprogramming, it excites in me a mixture of genuine sympathy and condescending pity (and if they're a coworker of mine, righteous wrath). I am glad I went through that phase in my late teens and had the nonsense shook out of me by my first professional experience as a programmer.
It's fine to pursue this as fun for its own sake if you are under no pretense as to its practical utility. But the danger of having your fun it in a bread-and-butter language like C++ is that you will be tempted to let it infect your workaday coding style. That's one of the perpetual dangers of hiring bright, overeager C++ programmers at the junior level.
Boost is a minefield. Its less booby-trapped libraries have made it into the next C++ standard library, but the more hardcore indulgences in metaprogramming like Spirit deserve all the scorn that is customarily heaped on them, if not more.
Your post is very condescending but also very light on facts. To someone successfully using C++ metaprogramming you just sound like you failed to find how and when to use it.
(I do agree that it can get ugly wrt error messages and syntax as neither the compilers nor the language were designed with metaprogramming in mind.)
The condescension was very much intended. Look, I'm not going to convince anyone of this point of view with a few hastily typed-out paragraphs on Hacker News. No-one can do that. It's something that rings true or false depending on your experience. My experience is that most uses of C++'s features beyond the rudimentary lead to software that takes forever to build, forever to run and forever to maintain, all of which are unacceptable and inexcucable. I started out in my pre-career as an enthusiastic C++ supporter who knew most of the tricks and dark corners of the language; template metaprogramming was (and still is) easy as pie for me. But every experience I've had since then has pushed me in the opposite direction, away from immature self-indulgent cleverness. Nowadays I mostly use C or a very C-like subset of C++.
Well I'm happy for you that you reached such intellectual heights before you were twenty - if only everybody else was as smart and gifted as you are, the world would be a much better place rollseyes
You can study programming languages themselves without misusing C++ features in horrible ways. Monads are already overused and misunderstood- using them in C++ templates just makes things worse.
They are an overly-specific interface to what can usually be done with applicative functors or even just functors.
This trend originated with Haskell and then when people who learned monads without understanding the other (often better) abstractions spread them everywhere else, we got things like this article.
Using monads, especially in a language like C++ templates or Scala, but also in Haskell, can overcomplicate things and make things longer than if you just solved the problem the direct way.
--
As an example of where monads are overused, look at the Maybe/Option type. I've read several articles that advocate using bind and/or Haskell's do-notation to move a string of null checks out of the main program.
This is great, but it's much more easily and expressively done with applicative functors. Instead of assigning all the intermediate results to names, you can just do
someFunction <$> Just 3 <*> Just "Something"
and it looks like regular function application. Because Applicative is a more general interface that Monad, it also has the plus of working with more types.
I agree that Applicative should be used when possible.
I think the Maybe example may be wrong, though. You may need the power of Monads sometimes. If you have a function that requires the result of a different function which was wrapped in Maybe, Applicative will not be enough.
Example:
xmlChild :: String -> Element -> Maybe Element
Now you might want to do something like:
myPath = xmlChild "a" >=> xmlChild "b" >=> xmlChild "c"
In Haskell, mostly. This is probably because most Haskell programmers learned about monads without understanding applicative functors, which are more general and a much better fit for a lot of tasks.
Frankly, I think that the big problem with C++ (and other larger languages) is the engineers who keep trying to use some minimal subset of it, pointing at the Blub+1 capabilities and screaming about how they can't understand it.
Lambda-calculus and monads are not necessarily Lisp-specific, neither would I call his syntax that (if only because he uses Haskell ->s instead of Lisp ()s :). See http://www.lfcs.inf.ed.ac.uk/reports/88/ECS-LFCS-88-66/index.... I liked the article, maybe it will be something to think about in some distant cpp. Although I tend to agree to your last argument about "using somebody else's DSL..." :)
OK, downvotes. Why, exactly? I'm not trying to troll. Monads as commonly explained on blog posts are cargo cult mathematics, and monads used in languages like C++ where they make code longer and more complicated instead of simpler, are cargo cult programming. I thought comments were for discussion, not silencing anything negative.
Dismissing a well-written and elaborate post in a one-liner with no convincing arguments is usually considered bad form.
Cargo cult programming itself seems to be an overused term. According to wikipedia it means "a style of computer programming that is characterized by the ritual inclusion of code or program structures that serve no real purpose. Cargo cult programming is typically symptomatic of a programmer not understanding either a bug he or she was attempting to solve or the apparent solution". I've seen this done. I knew programmers who would call the same (side-effecting) function twice in a row, "just in case, since it might fail the first time", instead of actually checking the function's return code etc.
Do you really think the post's author has no idea of what is talking about? That he copy-pasted some code and ended up with an EDSL in C++? The particular code he shows might not be practical, but that doesn't mean it's "cargo cult". For instance, perl-golf, obfuscated C contests and implementing X in Javascript aren't practical-minded activities either, but you can't call it "cargo cult": programmers engaging in them aren't exactly clueless.
Again, monads may be perceived as "hyped" but that doesn't mean they're "cargo cult mathematics" any more than the Y combinator is.
The application of monads to C++ metaprogramming could be described as cargo cult programming, even if this post is a well-thought-out explanation of how it works.
While it is true that they work to help organize libraries like Boost Proto, it is questionable whether that's the best or even a good way to accomplish that library's goals. There's a reason the metaprogramming-heavy parts of Boost are still generally avoided in many parts of the programming world.
I see this as cargo cult because it appears as "monads are cool, and C++ templates are a weird functional language, let's do it that way" without much regard to whether monads, template metaprogramming or EDSLs are the right tools here.
I guess we're using the term “cargo cult programming” about different things. I'm not arguing that the author of this post doesn't have a very good knowledge of C++, and a somewhat good knowledge of how monads are sometimes used in Haskell. The problem is that the way monads are sometimes used in Haskell is cargo cult mathematics (I'll explain in a moment), and the way he implements monads in C++ gains absolutely none of the benefits yielded by their use in Haskell, adding only useless complexity to his code. While this isn't an example of the incompetence you have witnessed, it is an example of ritual inclusion of program structures that do not yield any benefits.
Monads are an example of using patterns from category theory in programming. This can be done nicely in languages with the facilities to do so, like Haskell. For instance, Functors, Monoids, and Comonads are patterns that are sometimes used in Haskell that are taken from category theory. However, the way monads are often used in Haskell is cargo cult mathematics. Here's why.
In category theory, monads are defined as functors with two associated natural transformations. However, in Haskell, the typeclass Monad is not even defined as a subclass of Functor. Additionally, one of the two natural transformations is swapped out for another (join is replaced by bind), and both are misleadingly renamed (return and bind don't suggest their actual meanings). Also, monads are often overkill for the problems they are applied to. Rather than think carefully about which pattern to apply to the problem, programmers echo the trumpeted “Monads are the fundamental method of abstraction!” and use them in their code, for that is the right thing to do.
Monads are not actions. Monads are not defined as a way to thread state through code. The type IO a in Haskell is an action, and since the interface provided to this abstract data type is monadic, there is a great deal of confusion about what the properties of a monad are and what the properties of the IO a type are. This blog post confuses them.
Even though monads are misused in this way in Haskell, their use still brings benefits. This is because Haskell gives the programmer facilities to write code that works over every monad. However, this is not the case in this blog post. The code in this blog post is parametrized over the types returned by the actions, but not over the actual type of action. One can see where he is looking at Haskell implementations of the functions to translate into C++ that they aren't. This basically renders the monads useless.
Another telling example of how these aren't really monads is when he says “You might have noticed that I use the words “action” and “program” interchangeably, although, strictly speaking, an action is the contents of a program. However, this distinction is an artifact or a Haskell quirk — a monad can’t be defined using a type alias, so we need the Prog type to encapsulate the action. Curiously, we won’t have this problem in C++.” In Haskell, an instance of Monad needs to be a container type, because the two functions that are the fundamental definition of a monad operate on nested containers (unit puts anything into a container, and join makes a container of containers into a single container). Since his C++ code isn't doing anything like this, he hasn't made a monad at all. He's simply made an extra layer of complexity in compiling an AST into actual code at compile-time, a task which has nothing to do with the nature of a monad. I'd reckon one could write code for compile-time EDSL's in C++ using no monads at all that would be much cleaner.
Sure, you can make a structure and slap the label monad on it and use return and bind functions (which aren't really what a monad fundamentally is) to put together your code, but if you can't write code that works as well on that monad as on other monads without changes, you've accomplished nothing but useless complexity. In other words, including program structures because they are accepted as good rather than because they have any benefit.
Can you explain a simpler way to do expression style programming in C++ without using monads and haskell as a way to understand and explain? You can make an argument that you should have expressions like that in C++, but that is not the same as saying the monads made this code worse.
Also, it's a monad whether he mentions it or not. Can you reference a monad explanation that is not "cargo cult mathematics"?
I don't even think the author should be using monads for expressions how he was doing in Haskell, which arguably does have suitable tools for using monads in this way. Doing it in C++ is ridiculous. One can have expressions like that in C++ using C++'s many other abstractions, such as classes and inheritance. This will result in code much shorter, cleaner, more general, more useful, and more readable code than what is found in this blog post.
The author hasn't achieved the abstraction of a monad in C++ anyway; there's no way to write code that works on both this monad and others, which effectively makes modeling something as a monad as he has done completely without benefit. This would be quite accurately referred to as cargo cult programming.
A monad explanation that is not cargo cult mathematics would be one that explains what a monad is in category theory (a functor with two associated natural transformations), explains it as one of many other patterns, and explains why simply including monads and these other patterns in your code does not automatically give them the Mathematical Seal of Approval™, especially when using a language with no support for abstracting over them.
Neither did most of the other comments at the time of posting- "How long did it take you to guys to read this post?" and "This is fantastic" add about as much or less. At least this one was a little thought-provoking, even if it looked dismissive.
> Monads as commonly explained on blog posts are cargo cult mathematics, and monads used in languages like C++ where they make code longer and more complicated instead of simpler, are cargo cult programming.
I upvoted both of your comments because of that statement. Explaining your position (although, it is a fairly weak argument) is enough.
My trigger for cargo-cultism was him stating that you want to lay out your templates in Haskell first.
"Monads in C++" would be an encoding of the return/bind interface, which is possible (surprisingly, at least to me), and then writing a library of combinators that work with this interface and any instance of them.
What this article (and countless others) are about is a particular encoding of bind/return for a single type, and not the Monad abstraction in general.