Sure, we learned "p => q" in discrete math, but we also learned "assert(someObj && someObj.data)" in CS101 or CS102. I think you're right, it is clearer, but not worth sacrificing the near universal familiarity of boolean expressions. I'd accept a change like this in Python, but it feels out of place in C++.
Old comment, but that's not quite right. The equivalent for implication is "assert(!someObj || someObj.data)" which is much more confusing to read. It's saying if someObj exists, then it should also have data. Upon reflection, I think a better example would be something like "assert(someObj.type == SomeObjType::Float => !isnan(someObj.data))". Without the new operator, it would be "assert(someObj.type != SomeObjType::Float || !isnan(someObj.data))" which doesn't make the intent clear.
You are possibly right. But I really think the "or" form is genuinely easier to understand, even more so if you have less education in formal logic.
You might learn "x => y" in school (I didn't!) but if you did it was probably in the context of making implication (modus ponens). You are told p=>q, and then later you also find that p is true, so you can deduce that q is true. That's easy enough.
But figuring out whether p=>q is true is tricker (intuitively). Say someObj is falsey - is "someObj => someObj.data" true? Well, it is, because an implication p=>q is always true if p is not true e.g. "if normal dice rolls a 7 then the weather is rainy" is true because a normal dice never rolls a 7. Is that intuitively obvious for everyone? I can't believe it.
Is "!someObj || someObj.data" true if someObj is falsey? That is really easy in comparison - maybe not everyone will just see it at a glance, but you just check the two subexpressions one at a time, and immediately notice that "!someObj" is true. (What about if it's truethy? Also easy - the first subexpression is false, so you move on to the second one - it's only true if "someObj.data" is truethy.)
Because that would be false if someObj is falsy. someObj => someObj.data, or !someObj || someObj.data (which are equivalent) would evaluate as true if someObj is falsy.
Hah, I love that you can actually write implication as `p <= q` in the current language – except for the fact that that's actually p implies q and not the other way around. Reminds me of the little-known "down to" operator `-->` [1].
And confusable with lambda/function and pattern matching operators in other languages. Imagine only being an occasional C++ user, seeing =>, and thinking they added terse lambdas! You'd be in for a [big]<T>(){disappointment;}.
Pronounce it "or else", as in p or_else q. Hell, even re-using the else keyword mid-expression would work. Or you know, the ultimate in simplicity: don't implement this operator at all.
I agree with the other commenter that => would be better used for lambdas.
It's not really correct to say that there are only 2 binary boolean operators, && and ||. There are also two really important ones, == and !=. They just so happen to not just be boolean operators and I guess they have slightly different behavior if your "booleans" can be more than 1 or 0.
So many footguns in this proposal! From the misleadingly symmetric symbols <= => >=, to the unintuitive behavior (for non-mathematicians) in the case of negative left argument, to the similarity to operators in other languages. Short-circuiting is already possible with the well-understood (!A || B) notation, which has the added bonus of allowing (B || !A) as an alternative with the same truth table but opposite short-circuiting. This proposal saves one “!” character at a very high cost. Just… why?
I prefer compiler intrinsics to look like functions, not operators.
For example, BMI1 ISA extension defines bitwise and not instruction, available in C++ as `_andn_u32()` or `_andn_u64()` intrinsics. That’s a rare operation most programmers are unfamiliar with, but it’s trivial to search internets for `_andn_u32`. It gonna be hard to find anything good searching for `=>`
I thought there were calls to simplify C++? This feels like the opposite direction. I've programmed in C++ for >20 years now and I can't think of a single moment where I would have wanted this feature.
Think about your own life. Does a toilet simplify your life? When you need to go to the bathroom, it seems much better to go to the toilet than say outside during the cold having to dig a hole in dirt every time and bury it. Yet toilets and plumbing add engineering and sophistication to your house.
This is the paradox. Simple languages push complexity to the user. Complex languages on the other hand simplify the user's tasks.
Has to be the right sort of complexity, though, or it makes the user's tasks worse over the long term. Every C++ project is already a dialect unto itself and this proposal would very much not help the situation.
I've been doing C++ since the 98 standard and C++ today is far easier, faster, more productive than anytime in the past. My code often feels as simple as python but much faster. Especially now that there are libraries using more modern standards.
I've only been doing C++ since the 2011 standard, but my sense of the language's evolution over time sort of matches yours. If you bother to learn the most complex, least-obvious, newest way to do something, the language has gotten safer, more functional, and more powerful.
You're way more productive using the container-agnostic `std::find()` than rolling your own version of it, as you might in other languages.
BUT I wouldn't say simpler. Do you slather `noexcept` on every method you write and turn every `const` to `constexpr`? :p Some advocate for that, and the have-a-new-keyword-for-something-that-should-actually-be-the-default-(but-of-course-the-defaults-will-never-not-be-backwards-here-because-we-got-them-wrong-way-back-and-too-much-existing-code-will-break-if-we-change-them) game is getting a little crazy for me.
> Simple languages push complexity to the user. Complex languages on the other hand simplify the user's tasks.
I don't think this captures the uniquely awful combination of "object oriented" + "low level (manual memory management)" that C++ chose. I think that's the most inherently complex dyad of traits you can choose for a programming language, and I think it's why C++ is the most complex programming language that's ever existed. They even tried hiding the complexity in shame with implicit behavior, like the compiler synthesizing the "rule of 5" functions for you, meaning you have to go out of your way and mark them as "deleted" to tell it not to do that when you know it'll do it wrong and leak memory and you have to memorize what the "rule of 5" is. (The only other language I can think of that went down the same route is D, but I don't know how complex it is in comparison to other languages. Maybe Walter Bright can argue it's a counterexample to my argument cause it's waaay simpler than C++. But then I'll counter-counter argue that it has garbage collection. But then again that's optional. My brain hurts.)
That is C++'s fame, and it is indeed quite complex.
However people that assert that, usually never went through PL/I, ALGOL 68, Ada, Common Lisp standards, nor have realized how JVM/Java, CLR/C#, Python/StdLib have changed during the circa 30 years of their existence.
Programs must be maintained. This means that a programmer must be able to read a program and understand it. Forcing the programmer to understand implication makes no sense.
We do give up understandability already with templates. Templates can be a source of non-understanding, but also there is no replacement for templates. Implications don't need a new feature since ?: already solves this.
Compare this proposal to another weird C feature:
char \*a;
...
17[a] = 'b';
where, although the array operator is associative, it simply isn't used that way in code because it offers no useful feature. Therefore it is an insane feature which is harmless. The implication proposal is not harmless.
50 comments
[ 3.2 ms ] story [ 113 ms ] threadAnyone came up with some enlightening examples?
assert(someObj => someObj.data);
The cannonical way to write that currently would likely be something like:
if (someObj) assert(someObj.data);
assert(!someObj || someObj.data);
Which seems fine as it is.
I don’t know you, but I guess that’s because of familiarity from years of seeing this kind of thing.
I think “=>” is clearer because it is what (AFAIK) people learn at school.
You might learn "x => y" in school (I didn't!) but if you did it was probably in the context of making implication (modus ponens). You are told p=>q, and then later you also find that p is true, so you can deduce that q is true. That's easy enough.
But figuring out whether p=>q is true is tricker (intuitively). Say someObj is falsey - is "someObj => someObj.data" true? Well, it is, because an implication p=>q is always true if p is not true e.g. "if normal dice rolls a 7 then the weather is rainy" is true because a normal dice never rolls a 7. Is that intuitively obvious for everyone? I can't believe it.
Is "!someObj || someObj.data" true if someObj is falsey? That is really easy in comparison - maybe not everyone will just see it at a glance, but you just check the two subexpressions one at a time, and immediately notice that "!someObj" is true. (What about if it's truethy? Also easy - the first subexpression is false, so you move on to the second one - it's only true if "someObj.data" is truethy.)
assert(someObj && someObj.data);
So it’s !P || Q. Or P=>Q as the author proposes.
If(bValidate => IsValid()) { /* runs if either validation was unnecessary or passed */ }
And that would be equivalent to (!bValidate || IsValid()).
[1] https://stackoverflow.com/questions/1642028/what-is-the-oper...
I think it’s not an April Fool’s joke, but this looks awfully confusable with >=.
I agree with the other commenter that => would be better used for lambdas.
But !a() || b() does that. So it seems that the proposal isn't needed. a implies b should just be written !a || b.
I replaced the table "raining? umbrella? faithful?" with "raining? umbrella? dry?".
Then made sense in my head only if I write something like this:
dry = umbrella || !raining
If I use my umbrella. I'm don't care it rains. Otherwise I will be dry only if it does not rain.
For example, BMI1 ISA extension defines bitwise and not instruction, available in C++ as `_andn_u32()` or `_andn_u64()` intrinsics. That’s a rare operation most programmers are unfamiliar with, but it’s trivial to search internets for `_andn_u32`. It gonna be hard to find anything good searching for `=>`
This is the paradox. Simple languages push complexity to the user. Complex languages on the other hand simplify the user's tasks.
You're way more productive using the container-agnostic `std::find()` than rolling your own version of it, as you might in other languages.
BUT I wouldn't say simpler. Do you slather `noexcept` on every method you write and turn every `const` to `constexpr`? :p Some advocate for that, and the have-a-new-keyword-for-something-that-should-actually-be-the-default-(but-of-course-the-defaults-will-never-not-be-backwards-here-because-we-got-them-wrong-way-back-and-too-much-existing-code-will-break-if-we-change-them) game is getting a little crazy for me.
You have an example? Cause I don't see how this is true.
I don't think this captures the uniquely awful combination of "object oriented" + "low level (manual memory management)" that C++ chose. I think that's the most inherently complex dyad of traits you can choose for a programming language, and I think it's why C++ is the most complex programming language that's ever existed. They even tried hiding the complexity in shame with implicit behavior, like the compiler synthesizing the "rule of 5" functions for you, meaning you have to go out of your way and mark them as "deleted" to tell it not to do that when you know it'll do it wrong and leak memory and you have to memorize what the "rule of 5" is. (The only other language I can think of that went down the same route is D, but I don't know how complex it is in comparison to other languages. Maybe Walter Bright can argue it's a counterexample to my argument cause it's waaay simpler than C++. But then I'll counter-counter argue that it has garbage collection. But then again that's optional. My brain hurts.)
However people that assert that, usually never went through PL/I, ALGOL 68, Ada, Common Lisp standards, nor have realized how JVM/Java, CLR/C#, Python/StdLib have changed during the circa 30 years of their existence.
I run `pdfinfo` and found out it was created with LaTeX...I need its settings so bad...I adore good looking, elegant PDF files!
Programs must be maintained. This means that a programmer must be able to read a program and understand it. Forcing the programmer to understand implication makes no sense.
We do give up understandability already with templates. Templates can be a source of non-understanding, but also there is no replacement for templates. Implications don't need a new feature since ?: already solves this.
Compare this proposal to another weird C feature:
where, although the array operator is associative, it simply isn't used that way in code because it offers no useful feature. Therefore it is an insane feature which is harmless. The implication proposal is not harmless.