Skip to content

[BUG] == and = in declarations are confusing. #824

@msadeqhe

Description

@msadeqhe

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 error

Because 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions