C++17: I See a Monad in Your Future
bartoszmilewski.com
bartoszmilewski.com
http://www.reddit.com/r/cpp/comments/1z1b1q/c17_i_see_a_mona...
The author of the post is also participating there.
I'm currently working on a new project using C++11/14 and it's a pleasure to see more functional features that will allow to be more concise.
On the other hand, C++ is already a mess. It's not even a beautiful mess, but it is a very functioning mess. Adding more stuff to it just seems like heaping more things on top of a pile that's already falling over in pieces.
Granted, like most everything else in C++, it will probably be optional, and hey, if nothing else, having more (optional) features is very pragmatic.
Joking aside, Haskell is pretty pleasant to program in most of the time.
Haskell is pretty good at a lot of things, so don't misunderstand me, Haskell it's really good! But, at the same time, it still has lots of room for improvement. I guess perfection doesn't exist.
I think something like http://lambda-the-ultimate.org/node/1446 could be the way forward. (And would be achievable from Haskell with enough work.)
Either because they are new, because nobody use them, or because they are LISP.
I really don't like how no lisper thinks it's a good idea to improve LISP, but it's this way.
Then C++ is a too low level language. Memory management is a bitch to deal with. FP is comfortable only in garbage collected languages, or otherwise you need a really, really well designed library and unfortunately STL ain't it, as STL has been plagued with memory leaks for years and still is. And many things in it are simply broken.
For me the test for FP is pretty simple - a language is FP-capable if there exists for that language a comfortable and reliable library for persistent/immutable collections/data-structures. And this is in fact not enough, but rather the bare minimum. You simply can't talk about functional programming without immutable/persistent data-structures.
Can such a library be designed for C++? It's much harder to do it than in say, a JVM language, or in Javascript, or in any garbage collected language, because of the memory management. Persistent data-structures rely on structural sharing for efficiency and in turn you rely on the garbage collector to take care of the junk when no longer needed. As an exercise, try to implement an immutable data-structure in C++, even a simple one like a linked-list and watch in horror the output of Valgrind as you're using it.
I do think it's possible though, with a little care towards designing it. I think the TFA explains quite well the value of good design and even takes memory management into consideration. If more people actually do FP in C++, maybe one day we'll have an awesome standard and an awesome library that we could use for our FP needs.
With that said, I really hope they won't leave std::future as is and I hope they do improve it. Adding a broken interface to the standard is not OK. If you can't design well an interface, then adding it to the standard does more harm then good. On the other hand the commute seems to be aware that std::future needs fixing, so that's good.
Thanks!
Edited to add: Ah, it seems a template argument can't itself be a template, which would seem to rule it out (though I'm running on very little sleep, so I might very well be wrong).
#include <iostream>
#include <list>
#include <functional>
using namespace std;
// fmap :: (a -> b) -> f a -> f b
template <template<typename> class F, typename A, typename B>
F<B> fmap(function<B (A)>, const F<A> &);
// pure :: a -> f a
template <template<typename> class F, typename A> F<A> pure(A);
// appl :: f (a -> b) -> f a -> f b
template <template<typename> class F, typename A, typename B>
F<B> appl(F< function<B (A)> >, F<A>);
// join :: m (m a) -> m a
template <template<typename> class M, typename A>
M<A> join(M< M<A> >);
// mbind :: m a -> (a -> m b) -> m b
template <template<typename> class M, typename A, typename B>
M<A> mbind(M<A> &x, function<M<B> (A)> f);
template <typename A, typename B>
list<B> fmap(function<B (A)> f, list<A> xs) {
list<B> ys;
for(auto x : xs) {
ys.push_back(f(x));
}
return ys;
}
template <typename A> list<A>
pure(A x) { return { x }; }
template <typename A, typename B>
list<B> appl(list< function<B (A)> > fs, list<A> xs) {
list<B> ys;
for(auto f : fs)
for(auto x : xs)
ys.push_back(f(x));
return ys;
}
template <typename A>
list<A> join(list< list<A> > xss) {
list<A> ys;
for(auto xs : xss)
for(auto x : xs)
ys.push_back(x);
return ys;
}
template <typename A, typename B>
list<A> mbind(const list<A> &x, function<list<B> (A)> f) {
return join(fmap(f, x));
}
template <typename A> ostream &operator<<(ostream &out, const list<A> &xs) {
int i = 0;
cout << "[";
for(auto x : xs) {
out << (i++ ? ", " : " ") << x;
}
cout << " ]";
}
int main() {
list<int> xs = {1,2,3};
function<double (int)> halve =
[] (int x) -> double { return 0.5 * x; };
function<double (int)> quarter =
[] (int x) -> double { return 0.25 * x; };
function<list<int> (int)> repeatOnce =
[] (int x) -> list<int> {
list<int> ys = { x, x };
return ys;
};
function<list<int> (int)> repeatIfEven =
[&] (int x) {
if(x % 2) return pure(x);
return repeatOnce(x);
};
list< function<double (int)> > fs = { halve, quarter };
cout << "return 7 = " << pure(7) << endl;
cout << "xs = " << xs << endl;
cout << "fmap halve xs = " << fmap(halve, xs) << endl;
cout << "fmap repeatOnce xs = " << fmap(repeatOnce, xs) << endl;
cout << "fs <*> xs = " << appl(fs, xs) << endl;
cout << "xs >>= repeatOnce = " << mbind(xs, repeatOnce) << endl;
cout << "xs >>= repeatOnce >>= repeatOnce = "
<< mbind(mbind(xs, repeatOnce), repeatOnce) << endl;
cout << "xs >>= repeatIfEven = " << mbind(xs, repeatIfEven) << endl;
return 0;
}Btw, it's tempting to use std::function in the same way as you would use a function in Haskell, but it usually isn't a very good idea. In C++ std::function is for when you want type erasure, usually because you wish to store a functor somewhere with a uniform type. Type erasure in C++ is similar to existential typing + a type class in Haskell. You could say that all Haskell closures are implicitly using type erasure and then the compiler has to work hard to optimize away the extra boxing/indirect calls.
Here is another way to write map in C++:
#include <iostream>
#include <list>
template<typename F, typename A, typename B = typename std::result_of<F(A)>::type>
std::list<B> map(std::list<A> const & xs, F f)
{
std::list<B> ys;
for (auto const & x : xs) ys.push_back(f(x));
return ys;
}
int main()
{
std::list<int> xs{1, 2, 3, 4, 5};
auto ys = map(xs, [](int i) { return i * 2; });
for (auto y : ys) std::cout << y << "\n";
}
You can expect this code to run exactly as fast as if you had written the loop by hand instead of using the map template function.I think the main point to take away is that this is a case where a monad is the right abstraction and can be implemented very logically, but since C++ doesn't and cannot really speak about monads directly, there comes a lot of boilerplate and you lose a lot of "free" utility functions you'd get in a language that can talk about arbitrary monads.
this is valid C++14:
template<typename A, typename B>
auto add(A a, B b)
{
return a + b;
} map :: forall a b. (a -> b) -> [a] -> [b]
The important part here is the `forall'. This means, that Haskell guarantees that essentially the map function can not look at the elements of your list. The only way for map to produce a value of type `b' is to apply the given call-back on an `a'.So you know that `map' can not eg add up your `a's.
Compare C++'s templates and overloading, where more function definitions with more specific types have precedence over more general ones.
See the first answer of https://stackoverflow.com/questions/18180428/is-parametric-p... for some discussion.
Such a function is easy to write in C++, but the function prototype can not provide a strong guarantee that your b value will only be produced by your callback.
It doesn't provide any such guarantee, and I think that was the whole point.
One thing C++ templates can't express e.g. is polymorphic recursion. http://stackoverflow.com/questions/10321856/does-c11-support...
in all relative seriousness, would anyone care to put forward a concise definition of "monad" (for programmers, not math-category-theorists)?
A monad is any generic class that has a flatMap function and a factory function that together satisfy 3 laws (explained below)
flatMap is like map, except the function passed to flatMap must return a monad (of the same kind).
The factory can make a monad of a regular value (it wraps it)
For example, an array's flatMap function takes a function that takes an element and returns an array. You can return an empty array if you wish, or an array of two elements (of the same type). flatMap "flattens" the result - it returns an array of elements of that same type.
The factory function and flatMap must satisfy 3 laws: left identity, right identity and associativity. That is, flatMapping with the factory function should result with the same thing as what you started with; wrapping a value with a factory then flatMapping it should result with that same value being passed to the function you passed to flatMap; and it shouldn't matter whether multiple flatMap calls are chained, or nested.
Like design patterns in OO languages, it isn't immediately clear why its useful to give a name (or a type) to all generic classes that satisfy this "interface" called monad, but it turns out that the concept pops up everywhere, from arrays to futures to Maybe (to replace null values) to Either to Observable and so on.
Not sure if I managed to be correct, understandable, concise or neither :P
Foo<A>
and a function B f(A)
and apply it to get Foo<B>
Then monad gives us something called join, which lets us take: Foo<Foo<A>>
and turn it into Foo<A>
It's pretty obvious that a Vector of Vectors of A's can be turned into a Vector of A's by concatenating them into one Vector. Also optional (something I don't know much about, sorry) can be turned from Optional<Optional<A>> into Optional<A> if the outer actually contains an inner optional, and the inner optional contains an A.So with these two things, we can do things like:
Vector<B> f(A);
Vector<C> g(B)
Vector<A> foos = {a,b,c};
foos.fmap(f).join().fmap(g).join()
to get a Vector<C>. The result is basically considered all possible results of applying f to all the values in foos, and then applying g to all the values returned by all the calls to f. this is the non-deteminism monad, where each function f and g can produce multiple results (including none) for each input, and we can chain multiple non-deterministic computations to get all the non-deterministic results.Usually Mopnad is explained with return and bind, but I think it's a bit less obvious. But I'll give it a shot...
bind in Haskell is something that takes something of type
Foo<A>
and a function with type Foo<B> f(A)
and produces something of type Foo<B>
From above, we can rewrite the example with Vector<B> f(A)
Vector<C> g(B>
Vector<A> foos = {a,b,c};
foos.bind(f).bind(g);
and get the same result as before. I guess you could use slightly cleaner syntax (why the hell not overload >> for yet another bizarre use right? [leading to foos >> f >> g;])I hope that you can see that this is just a simple interface which can work for many types that look like Foo<a> (Vector, Set [assuming C++ has that?], Optional, even pointers to some extent; see the discussions of the introduction of "?." into C# which is exactly what the Maybe monad encodes). There are also plenty of other monads in the haskell universe which aren't as easy to show in C++ because a) I'm not sure C++'s type system is up to it and b) my C++ knowledge is not strong enough to demonstrate things like the State monad, and definitely the Cont[inuation] monad.
Anyway, bring it back to the topic of the article, it should be clear that something of type
Future<A>
can be composed with other functions w3hich produce more Futures: Future<B> f(A);
Future<C> g(B);
Future<A> async_A("thing");
asyc_A.bind(f).bind(g);
Which handles all the unwrap and passing of values "inside" the future to each consecutive function.I know this isn't a concise explanation, but it's because it's a very simple, but also very abstract idea, and its best introduced with example uses, then generalising it.
He wants a jit for cpp?
Boilerplate prevention, I think.
I'm sure that's not the actual state of things, but goddamned if that isn't what appears to be happening.
EDIT:
Before downvoting, consider the elaboration I've replied below with (and then you can at least downvote that too!).
EDIT2: HN wouldn't let me submit this, so is edit:
The basic issue is not that futures/promises/monads aren't awesome--I'm quite, quite sure that they are the future (harhar) for solving a lot of things. I use them extensively, for example, in Javascript. They're Good Things (tm).
It's not that template metaprogramming is bad, or not useful, or that Boost isn't our lord and savior for greenfield C++ code.
The problem is that this shit still has to interoperate with code written more than two decades ago.
C++ began life as a weird ball of OOP and templates slathered over C. It failed to fix some of the real, true, awful parts of C (pointers, for example), and those are still around.
Pray tell me, why do I want a language which has both atomic exchanges (sorta, kinda, depending on your compiler and environment) and monads and function mapping? Doesn't that strike anyone else as, oh, I don't know...ill-focused?
C++ is simply too large a language to recommend to a beginner, too difficult to actually make guarantees about (when you can at all!), and in general the whole rotten mess should be cut in half.
Into, perhaps, C, and Haskell.
Is it the naming convention? Functor, Monad, Applicative, bind, join, fmap etc? Yes, Haskell can be a bit confusing at first. But if you don't like them you can urge the standards committee to use different names.
Or is it the concepts themselves? That would be pretty sad. A Functor is a really basic concept. You probably use it every day, in one form or another. I'm pretty sure of that. Once you understand this basic concept, you can start building the others on top of that knowledge. It's all about building a common language and understanding of these basic concepts.
The most useful name is probably somewhere in the middle. It may not necessarily consist of everyday words, but it probably relates very obviously and clearly to the C++ functionality.
"Monoid" is a very detached term. It doesn't obviously relate to the C++ construct or concept the way that terms like "typedef", "template", "stream" and so on do for other constructs/concepts.
Such as?
"Monoid" is a very detached term. It doesn't obviously relate to the C++ construct or concept the way that terms like "typedef", "template", "stream" and so on do for other constructs/concepts.
Well of course! Monoid is far more general than those other concepts. It's also a far better name, being unambiguous. Those other names are reused all over the place in different contexts and their meanings subtly shift between their uses. Monoid is not so overloaded; once you learn it, you know what it means every time you encounter it.
However, I would like to think that there's somebody out there who could come up with a name that describes the concept sufficiently, while still using pragmatic C++-style terminology.
All I'm saying is that "monoid" is terminology of a style that's very different from basically all other C++ terminology. To many C++ programmers, even those with a background in mathematics, it's no better than gibberish.
At least the existing C++ keywords and terminology tend to be far more descriptive of what they're referring to, even if there is ambiguity in some cases. This is true even for the more abstract concepts in C++.
What does that mean? How is it any more detached than object or method?
An "object" is something specific that exists (implying it can likely also be created and destroyed), and can be distinguished from other objects. This applies equally well to a combination of a data structure and some related code as it does to a tennis ball, or to a car, or to a book.
A "method" is a systematic way of doing something. That "something" could be the operations described by some source code, or it could be tying a shoe, or even brushing one's teeth.
"Monoid" has no good real-world parallel. It has no related or relevant meaning outside of very, very specific contexts. That's what makes it "detached". It's off on its own, with an isolated and non-obvious meaning.
The Haskell people share your concerns. Simon Peyton Jones famously suggested "our biggest mistake [in designing Haskell was u]sing the scary term 'monad' rather than 'warm fuzzy thing'". (https://research.microsoft.com/en-us/um/people/simonpj/paper...).
It's jargon, but it's jargon shared across a few fields, which makes it more worth learning. And while it lacks a "real-world parallel" it has a lot of simple examples. Lists over concatenation, integers over addition, positive integers over max, negative integers over min... It's interesting and informative to observe why integers over max (or min) is not a monoid but is a semigroup.
Also, this objection would seem to apply equally well to functions and variables as monoids.
And the same holds true for "variable". It's a very common term for things that change their state of being, including stuff that isn't directly related to programming or mathematics. Stuff like the weather, somebody's mood, and so on.
"Monoid" just doesn't have any everyday meaning like those terms do, especially a meaning that so closely resembles the programming concept like in the case of "function" and "variable".
Monoid, on the other hand, has no such problem. If you look it up, its meaning in mathematics directly translates to its meaning in Haskell.
Yeah, they mean yet something else in statistical software.
Most people are inherently familiar with the concept of a "container", in the sense of something that holds other things, regardless of whether it's a physical container or a data structure.
"Iterator" might not be as clear as "container", but at least most people are familiar with the concept of doing something repeatedly, whether it's some physical action or executing some chunk of source code.
"Monoid" just isn't like that. It may be concrete when constrained to the context of mathematics or programming, but it's quite meaningless outside of those very specific contexts.
But that isn't really true. Addition and multiplication are both real-world examples of monoids that everyone is familiar with (obviously, these aren't physical things, but I don't see why that's relevant).
Monoid is NOT a detached term it says I have a type with an identity element, and an associative binary operator, and that that (id `op` elem) always is elem. Try to tell me that i complicated in the slightest. I can most definitely not give you a single line description of templates, or most C++ features. Abstraction makes things more general (i.e less specific) meaning it is easier to describe, reason about, and discuss because I can only talk about things that hold universally.
If you doubt me let's show some things that form a Monoid right now, and how easy it is to define.
1) Strings - identity element = "" - associative operator = +
2) Lists - identity element = list<A>() //empty list - associative operator = concat
and we can go on and on finding instances of Monoids simply from a one line description.
For most C++ terminology, you don't need to give a single-line description. The keyword or concept name alone generally embodies that information very well. To use your example, at a basic level a C++ "template" is quite similar to a form letter, stencil or other real-world "templates". It's a predefined mold that you inject your specific information/material into to get a final product with minimal effort.
There just isn't a direct relation back to the real world with a term like "monoid". You end up with people who are confused at best, or more likely they're thinking of some other word that sounds similar but is totally unrelated.
And that might even be a good thing. The stencil analogy for C++ templates gives you a warm fuzzy feeling, but doesn't actually help you program. Contrast: the one line definition of Monoids is all there is to them. That's all there is to know about Monoids.
Just for the record, I'd like to state that I couldn't make heads or tails of this explanation. 'Identity element', when thinking of C++, just makes me think of a hash function or something.
Looking at the examples, it's sort of clearer— a monoid is a type that can hold a value that's empty (or zeroed, or whatever). But that doesn't change the fact that that explanation (and the term 'monoid') is so firmly rooted in math jargon that it's basically incomprehensible to someone used to thinking in C++.
It's firmly rooted in like 5th grade math. Every child in the US that takes basic algebra in school learns about identities and identity rules like "0+x=x" or "1*x=x" or in pseudocode as something like "(id `op` elem)=elem".
Possibly, C++ should adopt the name anyway, but it's by no means a commonly used or understood term by most users of C++. As has been mentioned, C++ is above all a language whose design is driven by pragmatism, so if another more common term can be found for the idea of a monoid you can bet it'll be preferred (even at the expense of some literary accuracy).
Classic. Sometimes Haskell fans have useful things to say, and sometimes they're just using terminology to try to win an intellectual dick-size war. The tricky part is telling the two apart.
Imagine trying trying to describe a monoidal category or topoi in the OOP style, it's like trying to describe what a Fourier transform is to someone who insists on using roman numerals.
Or describing Latin declensions to someone who reads kanji. The key is that, if the audience admires Latin speakers, you can appear smart.
I think the abstract algebra names are great because they describe the general case precisely without any preconceptions carried along with them.
And there's papers about interesting things you can do with them.
I mean, some concepts are clearly not able to be explained to the layman; you need some background. But do people really need to learn abstract algebra to be able to get their heads around this stuff? Are there no better terms that communicate to more people?
And, given that that's the problem, clearly dog breeds are not the answer either.
I'm not a C++ programmer; I don't know what half the names are from the GoF book. Does that make them bad names? No! Since when should a word have to carry around its own definition so that people who've never seen it before automatically know what it means?
I mean, some concepts are clearly not able to be explained to the layman; you need some background. But do people really need to learn abstract algebra to be able to get their heads around this stuff? Are there no better terms that communicate to more people?
Concepts like Functor and Monoid and Monad are extremely simple. You don't need to go back to school to learn them. You only run into trouble if you expect them to be explainable by analogy to a more concrete notion.
And, given that that's the problem, clearly dog breeds are not the answer either.
It was a sarcastic remark but it highlights an important point I want to make. Assuming you knew nothing at all about dogs; if you were given a list of dog breeds and a stack of photos of dogs, would you be able to match the breed names to the photos? No, probably not. That doesn't mean their names are bad; you simply lack the experience to figure it out.
>Concepts like Functor and Monoid and Monad are extremely simple. You don't need to go back to school to learn them.
The issue here is not at all how complicated the concepts are. The issue is how common and/or descriptive the name for them is. "Monoid", to someone who hasn't studied abstract algebra, is neither. Something like "Appendable" might be more appropriate for C++.
So I can append False to True to get False (Boolean monoid under &&) or append 8 to 9 to get 72 (integer monoid under multiplication)? There's also a monoid instances for any single-argument function into a monoidal type, where (f <> g) x = f x <> g x; I don't know what to call that but it's definitely not appending.
Append is a name that works in a few cases but horribly breaks down in the general case.
Oh come on, man! I can't believe this ridiculous discussion. I'm not lashing out at you, don't get me wrong, but seriously, you think you need to study abstract algebra to understand what a monoid is? Um, I remember one of the first things mentioned in the, literally, first lecture in undergraduate course on calculus was a monoid, among other algebraic structures. Granted, it was theoretical physics, but still, I doubt any respectable university teaching CS would fail to give students at least some familiarity with basic algebraic structures. But you don't even need a university education to grasp this, seriously, stop being afraid of precise terms, there's nothing scary behind them.
EDIT: And to actually address your point :), "intuitive" can only go so far. Sometimes there is simply no intuitive term that can do justice to the concept at hand. As Feynman once said: "I'm not going to lie to you, I'm not going to tell you it's like a ball bearing on a spring because it isn't." Especially in programming I'm not convinced that intuitiveness of a mere label for something is a valuable goal. You can't program on intuition, at some point you just have to learn how the thing really works.
No no no. My whole point is that while the concept is not complicated, that particular term ("monoid") simply isn't common.
I agree with your edit section though. And I don't necessarily think C++ shouldn't adopt the term 'monoid' due to its relative obscurity (especially if no better term can be found); I just think it's got some figurative points against it for that reason.
DRY is not abour reusing names from other fields. Perhaps you were going for the "principle of least surprise".
But given that most people are not familiar with those Math names in the first places (including mathematicians not versed in type theory, trust me, I've asked a few Math colleagues from university and it's chinese to them too), I doubt even that applies here.
So, no, some arbitrary names like Monoid and co, obscure even for people with a passable Math knowledge, picked back in the day, is not the best naming scheme for that behavior that people can come up with.
The best that it has going for it is that is has been used in Math "for the last 50 years" (still, hardly a "staple").
A lot of math naming and notation is arbitrary and a historical accident. That was one of the things the "axiomatic language" guys tried to solve back in the '20s after all.
Many computer scientists already understand reasoning about abstract structures and use them in their work.
For example commutative operators are incredibly powerful tool in distributed systems, as I am able to disregard ordering. In general it is useful to be able to say that if I meet an abstract specification (i.e a Monoid, Group, Ring, ect) because it allows me to perform operations with confidence that they are correct.
If you doubt the usefulness of this just look to Twitter (can't get much more "real world" than that) and see how they have been leveraging abstract reasoning in projects like Summing Bird (and its sub-projects like Algebird, Bijection, ect).
Well, I asked undergraduate math students on their final year, and they didn't know. Perhaps all PhDs in math do, but that's not much of an argument in reusing those terms in CS.
Myself had several classes of algebra in different years in university (for the CS degree) and we didn't ever talk about Monoids and such.
I have no reason to not believe you, but how is this possible, and what does it say about either those students or that university or even both? Do they also not know what a group is? A ring, a field? How can a mathematician (and a nearly graduated student in maths is certainly a mathematician) not know what basic algebraic structures are? I'm shocked, frankly. It's literally equivalent to saying they don't know what real numbers are, without exaggerating one bit.
They should know what a group is, because even us, CS students, were taught group theory for a whole semester.
As for the monoid thing, I guess they could have been taught at some point, along with being taught 20 other branches and fields of mathematics in different classes, and they were more likely to remember a) the basic stuff, b) the stuff they started to specialize on and concentrated upon writing their thesis. So, I guess if I had talked to someone who had taken a preference to type theory and such, they would have known.
>How can a mathematician (and a nearly graduated student in maths is certainly a mathematician) not know what basic algebraic structures are? I'm shocked, frankly. It's literally equivalent to saying they don't know what real numbers are, without exaggerating one bit.
Is it? I don't know. You can read whole books in Algebra and not mean the word "Monoid" once in them, including some university books.
For example (not even one mention): http://www.amazon.com/gp/search?index=books&linkCode=qs&keyw...
This again no mention: http://www.amazon.com/gp/search?index=books&linkCode=qs&keyw...
This (a "graduate level" book) only mentions Monoid once, somewhere on the first pages, and adds that it won't be emphasised: http://books.google.com.sg/books?id=C4TByeUh9A4C&printsec=fr...
And I'm pretty sure in our Algebra books (for CS students, 2 classes in the 1st and 2nd year) there was no much mention of monoids (or, possibly, the professor skipped over those chapters, maybe had some brief explanation and went on to other things).
Of course, what university gradutes of a field do not know can sometimes be even more daunting. For example, a surprising percentage of degree-ed Computer Scientists cannot even solve the fizz-buzz problem:
Hm, I just gave a quick glance to one group theory textbook (the only one I have in English), and to be honest in the first 20 or so pages there is no mention of the word "monoid" (as far as I can see, I really just quickly scanned through the first chapter) but it does mention semigroups (which are just an identity element away from monoids)...
> And I'm pretty sure in our Algebra books (for CS students, 2 classes in the 1st and 2nd year) there was no much mention of monoids (or, possibly, the professor skipped over those chapters, maybe had some brief explanation and went on to other things).
Yeah, and that's fine, but for maths students to not even remember that it was mentioned is kind of sad. It shows they don't really care that much about what they're doing. If it wasn't even mentioned by the professor, shows a disinterest on his/her part to actually teach concepts. It's always much better when teaching a concept to also at least mention related concepts or similar ones at different levels of abstraction, that gives a complete picture in which you can neatly place things. A group doesn't just appear out of nowhere (although it certainly can be defined on it's own), and it's not alone...
> For example, a surprising percentage of degree-ed Computer Scientists cannot even solve the fizz-buzz problem:
Sigh... I know. But you see, that's why I found this discussion about the unintuitiveness of the word "monad", here of all places, absurd. I wouldn't expect people who cannot solve fizz-buzz to have any clue about anything, and on some level I'm fine with that, this is not an elitist rant, but people in a place like this? After all, programming routinely involves dealing with concepts that are much more unintuitive that a monoid, IMHO.
Some of those words are very likely non-mathematical words that were adopted and re-purposed by mathematicians. "Tree" is inspired by how the data structure's shape is very similar to that of trees (that is, the plants), for example.
Edit: Yes languages get this wrong but I was focusing on the borrowing of vocabulary more so than the correct usage of it. Many words are abused like Functor to mean function object in C++, functions referring to procedures, ad-hoc vs. parametric polymorphism, strong and weak typing ect.
Oh, and lots and lots of your peers are formed in an specialization of mathematics called "Computer Science".
One might as well say that biology is a branch of philosophy, because it was the source of all the natural scientists; but such a description, in modern times, would not be particularly informative.
They're able to turn a lot of algorithms inside out; make the kernel a simple function of values where they used to be threaded through the code flow, and use things like bind to mix them back in. This makes algorithms testable and more composable, and raise the level of abstraction in the rest of your code so that you're operating more at the DSL level for your domain, whatever it is.
It's worth learning the concepts. Once you do, you'll be surprised that there's so little "there" there.
And their commonality and generality are exactly the reasons why they should have short yet precisely general names.
I've been using Scala full time for a couple of years now, but it took me ten months to really understand not just what monads are, but how they are useful in expressing a program from a different perspective to other approaches, and the benefits that come from that. Now I use them all the time and see how they add huge benefits to my code.
In all the time that I've been working with Scala, and building my knowledge and experience of how to apply functional principles usefully, I have yet to (knowingly) use a Monoid, or encounter a situation where it was clear that they would have a benefit. I'm not saying that there are no such situations, and it may be that there have been situations where they would have been useful, but I didn't know it, and I would love to be able to add them to my toolbox. However, I do see that a lot of the time these concepts are introduced in a very abstract way and what it missing is not the simple definition, but rather some clear idea of when, how and why to use them.
Personally, I don't think you'll ever reach perfection if you restrict yourself to adding features. Sometimes you've got to take them away and that's not really feasible with a mature language like C++.
You don't like this? Fine. It's pretty easy - don't use futures, and it won't be in your face. You can ignore it completely and use the rest of C++ as you see fit.
I'm curious because you seem to have made two different points: 1) it doesn't make sense because adding new features to such an old and widely used language is hard, and 2) it doesn't make sense because of the kind of language C++ is. (1) isn't true for Rust, but (2) is.
As for your observed point 2 though: at some level, systems languages need to allow the twiddling of bits on and off, perhaps allow the direct interaction via assembly with the processor, have some notion of native word size, and perhaps also make simple common idioms in low-level programming (stacks, interrupts, and so forth).
It is very rare (in my experience, which may not be accurate) that these issues should be even visible at semantic layer concerned with mapping functions over data and other functions, with sorting sets, and sending messages. It's simply too polluted a headspace.
At its best, it provides tools for building cost-free abstractions that hide bit-twiddling and assembly hacks while providing an expressive set of building blocks for the semantic layers above them. In this case a programmer only thinks about one layer at a time, so the mapping and messaging can be tuned to their domain, but the implementation is abstracted.
At its worst, it provides the verbosity of Enterprise Java with the memory safety of C and it becomes very difficult to trace what the program is doing and how. I have found this is very strongly related to the widespread use of non-smart pointers. This lets the semantic domains smear into each other and tends to produce astonishingly brittle code.
Also it's amusing you opine that pointers are "awful" in the same post in which you rail against others' for "screw[ing] up C++". Pointers are one of the best things about the two languages in my opinion. I love the power and flexibility they provide.
All 10 of them? You do understand that these C++ proposals and additions have nothing to do with Haskell people, but come from seasoned C++ veterans, like Herb Sutter and such.
A while ago the FP people figured out that their `hacks' to do IO actually conform to the structure of a Monad. So they exploited the fact. Now the author sees that the `hack' to do futures in C++ also conforms to the structure of a Monad, and suggests exploiting that fact.
Yes, though futures were always like that when you look at Java for example. Java always suffered from the need to prepare complex design patterns to do simplest things as it was much less flexible than C++. Now C++ is getting there as well.
I personally would stick with some earlier C++ standard and use Haskell for my functional programming needs. Feels much more natural than trying to bend each language to support every thought process imaginable, and doing a poor job at that.
Dunno, but I think part of the reason Rust is popular lately is that it combines safety and functional features with high performance by default and the ability to /theoretically/ easily drop down into lower-level code to eke more out.
I think C++ is an unnecessarily complex language for various reasons, but adding some basic functional methods to a STL class isn't the end of the world on that front and sounds fairly useful.
p.s. Java has atomic exchanges.