-
Notifications
You must be signed in to change notification settings - Fork 264
Description
Description
Considering @hsutter's comment from discussion #623:
Thanks! I think I've answered this, and addressed it for
<<
's interactions with comparisons, in the comment thread starting here: #817 (comment)Briefly: It's reasonable for
<<
with its built-in bitwise meaning to be high-precedence, but in the 1990s overuse of overloading was popular, including its use for streaming I/O which inherently wants to be low-precedence but can't change the actual operator's precedence... and that overloaded use of<<
became popular so we keep having this surprise. In fact, I think<<
is the poster child for precedence issues, because it's the major case that invented a later use of the operator for something very different, that became very popular.Operator overloading is fine, but I view
<<
for streaming as a warning example about why we shouldn't overuse the feature, and when we do use it we should closely follow the built-in operators' meanings... as Scott Meyers would say (and did), "do as the ints do."
The part "we should closely follow the built-in operators' meanings" is important here.
To Reproduce
Notation ==
Considering type and namespace aliases:
v32: type == std::vector<i32>;
A typical C++ programmer expects ==
to return the result of a comparison, but it sets v32
type definition from another type instead. Therefore ==
is an assignment operator in declarations, but it is a comparison operator in expressions.
Considering constexpr
functions and variables, ==
is more confusing:
fnc1: () -> bool == (x == y); // =='s are not the same!
fnc2: () -> bool == x == y; // Should it be allowed?!
The first ==
sets fnc1
function definition, but the second ==
compares two literals and returns the result. A typical programmer may ask why it's happening while they are next to each other.
Considering concept
declarations in Cpp2:
arithmetic: <T> concept = std::integral<T> || std::floating_point<T>;
Unlike types and namespaces, it uses =
instead of ==
. Cpp2 is/was designed for C++ programmers of which they are more familiar with declaring stuff by =
than ==
in Cpp1:
using v32 = std::vector<i32>;
template<class T> concept yes = true;
Notation =
Considering function declaration syntax:
fnc1: () = {}
fnc2: () = something();
If we ask programmers who are not familiar with Cpp2 that "What does fnc1
function do?", they probably will answer fnc1
returns an empty list {}
. Because that's natural to programmers. =
puts the result of its right side to its left side. {}
is on the righ side, so it must have a value.
But this known behavior doesn't work in Cpp2. =
doesn't set anything in the example, and {}
doesn't have a value too! Of course void
is nothing, it's not even similar to null
and nullptr
which they are values.
EDIT: Considering this note from this comment:
I mean
= {...}
doesn't work in Cpp2 generally:x: TYPE = 10; x = { /* ... */ } // Syntax errorBecause it's meaningless to put a block statement in a side of assignment.
Looking at fnc2
, a typical programmer which is not familiar with Cpp2, thinks something()
has a value and the function returns the value from the right side of =
, that's expected because of the assignment operator in the declaration. Although we had this natural behavior in Cpp2 before, but @hsutter changed this behavior (because of issue #257) because of the push-back he got on issues/comments as he explained it here.
Why not bring back the original behavior for function declarations with = expression
only?
Additional context
To solve the issue, Cpp2 may have a simple defined rule to separate statements from expressions in declarations, if one extra =
could be dropped from them:
fnc1: () { print(10); } // A block statement
fnc2: () = 10; // An expression
fnc1
is defined by a block statement, because it doesn't have =
, but fnc2
is defined by an expression, because it has =
. This rule allows to categorize function decleration to two syntaxes.
First, the function declaration (also lambdas) with block statement: it has not a return type by default:
fnc1: () -> void { print(0); }
fnc1: () { print(0); }
fnc1: () print(0);
Second, the function declaration (also lambdas) with an expression after =
: it has a generic return type by default:
fnc1: () -> _ = 10;
fnc1: () = 10;
In a similar manner, types and namespaces would be (more familiar to C++ programmers):
cls1: type { ... }
cls2: type = cls1;
nsp1: namespace { ... }
nsp2: namespace = nsp1;
In general {}
is a block statement and = ...
is an expression or a name (or a fully qualified name).
Also for constexpr
functions and variables, other solutions such as using a keyword can be considered.
Discussions #742 and #714 are related to this topic. Thanks.
Follow-up Readings
More discussion about how the new syntax may improve Cpp2 in this comment:
- Consistency with Control Structurs
- Consistency of Variable and Function Declarations (and
inspect
Expressions) - Avoid Lambda Syntax Ambiguity
"Why is it a bug?" from this comment:
- Why has it to be fixed?
- What if we drop
=
from them?
"Why the new syntax is necessary?" from this comment.