Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate conditional operator ?/: #740

Closed
Cat-sushi opened this issue Dec 13, 2019 · 20 comments
Closed

Deprecate conditional operator ?/: #740

Cat-sushi opened this issue Dec 13, 2019 · 20 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@Cat-sushi
Copy link

Cat-sushi commented Dec 13, 2019

Motivation

NNBD, which is introducing many notations with ?, makes the ternary operator ?/: an eyesore more and more. For example, when null aware subscribing operation has shape of a?[b], then {a?[b]:c} is ambiguous at least mentally. The same thing goes for invocation {a?(b):c}.
In addition some proposals such as #357 might make the situation worse and worse.
T?, ??, ...?, and so on are OK, but ...

Note

This issue is derived from #376 and #729.
This issue aims to alternate the ternaly operation a ? b : c.
This issue doesn't treat collection-if or if-statement, directly.

Candidates of syntax

if(a) b else c // 1
if(a, b, c) // 2
if(a : b : c) // 3
and more

@Cat-sushi Cat-sushi added the feature Proposed language feature that solves one or more problems label Dec 13, 2019
@lhk
Copy link

lhk commented Dec 13, 2019

I'm very much in favour of doing something about the ternary operator.

Now, before NNBD syntax becomes sprinkled all over existing codebases, seems like an ideal time to let dartfmt do the heavy lifting of a syntax change. The ternary operator is still unique in its syntax and I think it should be possible to come up with a reasonably clean replacement.

That being said, using something based on if brings in lots of potential clashes with if statements, expressions and collection ifs. I think this is another corner of the language spec that is now proving to be kind of a dead end. It would be awesome to get if expressions. But collection-ifs will cause ambiguitiy. :(

@lrhn
Copy link
Member

lrhn commented Dec 13, 2019

I'm not sure the ambiguity is that much of a problem for collection-ifs.
It may make parsing harder because you have to parse-ahead for a while before being able to distinguish the expression from the collection element, but in case of a full-on ambiguity, where the same text can be either, it likely also doesn't matter which one it is.
Because a list/set collection element can be an expression, it wouldn't matter whether you do "produce (if test a else b)" or "if test (produce a) else (produce b)".

Example:

var x = [ if (test) 1 else 2 ];

Here it's ambiguous syntax, but the behaviors of the two options are indistinguishable.

We'd obviously have to go through all the syntax corners to make sure there are no cases I have missed.

@Cat-sushi
Copy link
Author

Note: This issue is not for collection-if. Please discuss it at #729 or post a new issue.

@Cat-sushi Cat-sushi changed the title If-expression without ? with mandatory else-part Deprecate ternary operator ?/: Dec 14, 2019
@lrhn
Copy link
Member

lrhn commented Dec 15, 2019

The issue with collection-if here is that expressions occur in collections too, so there is some amount of syntactic overlap/ambiguity if expression-if starts using the same syntax. There is no need to change collection-if, and as stated this issue is not about doing so anyway, but changing expression-if interacts with collection-if, and that interaction needs to be discussed. (As stated above, I believe that interaction and ambiguity to be not-too-problematic).

It's true that removing ?/: syntax will free up some syntax that is otherwise causing us problems with syntactic ambiguity, and using a prefix if will definitely work instead.

The options are really:

 <expression> ::= `if` `(` <expression> `)` <expression> `else` <expression>

or

 <expression> ::= `if` `(` <expression> `)` <expression> (`else` <expression>)?

Anything else will just be inconsistent.

Since expressions must evaluate to a value or throw, omitting the else branch means that the omitted branch needs a default semantics.
The three obvious options are:

  1. Do not allow omitting it.
  2. Evaluate to null
  3. Throw.

The first one is what we currently do for ?/:. It also avoids ambiguity in if (t1) if (t2) e1 else e2 - which if does the else bind to? We solved it for statements, so we'd do the same thing for expressions, but it's a place where users can write code that doesn't do what they think.

The second one is adding null values, and it becomes ambiguous when an if occurs in a collection: Interpreted as an expression-if, it would add null to the collection, interpreted as a collection-if it adds nothing. So, not particularly useful.

Throwing means that it's effectively an error, and we should probably have gone with not allowing it anyway. Not practically useful.

So, my vote would be on requiring an else branch.

Ob-pedantry: The ?/: operator is not the ternary operator. It is the conditional (expression) operator which is a ternary operator (takes three operands).

@Cat-sushi
Copy link
Author

The issue with collection-if here is that expressions occur in collections too, so there is some amount of syntactic overlap/ambiguity if expression-if starts using the same syntax.

I understand.

So, my vote would be on requiring an else branch.

I would also vote on requiring else branch. (The first title of this issue was "... mandatory else-part")

A syntax clean braking from if-statement and collection if is also attractive for me.

@Cat-sushi
Copy link
Author

Cat-sushi commented Dec 16, 2019

@tatumizer null doesn't necessarily mean omitting argument, so your proposal require special syntactical treatment which could be called if-argument. If-argument which doesn't exist now make discussions more comprecated. I'd like to discuss deprecating ?/:.

@Cat-sushi
Copy link
Author

I prefer if(a, b, c) as conditional expression, so far. It is a little confusing because it start with if(, but it's syntax is not ambiguous.
Is it feasible?

@lrhn
Copy link
Member

lrhn commented Dec 16, 2019

Using if (e1, e2, e3) is technically feasible. I personally think it's too far from the existing syntax to be preferable.
One design rule is that similar things should look similar, different things ahould look different.
This looks like a function call, but isn't. That's not unprecedented (assert is also not a function call), but it is one argument against. It would make the conditional expression easily delimited and easier to parse. It might confuse users coming from JavaScript, where you have the comma operator, and if (a, b, c) means if (c) after evaluating a and b for their side effects.

I can see the reasoning for "optional arguments". A non-else-condition in an optional argument position could mean not passing the argument, but currently it would only work for named optional parameters. Omitting a positional parameter would shift the remaining arguments up, which is not sound in general. If we had a way to omit optional arguments in the middle of a positional parameter chain, then it would fit better (not shift later arguments up, but still trigger default value in the function). That's a different feature.

@Cat-sushi
Copy link
Author

Is comma operator in JavaScript or C populer for modern language like Dart?
Anyway, I would also like see clearer syntax for conditional expression than f(a, b, c).

@Cat-sushi
Copy link
Author

What do you think about if(a : b : c)?

@Cat-sushi
Copy link
Author

Cat-sushi commented Dec 17, 2019

@tatumizer
Set literal is one of the major reasons which makes the discussion #376 complicated. AFAIK, Set literal was newly introduced on 27th February by demand from the Flutter team, but the Flutter framework uses only type specified Set literals. So, deprecating Set literal without type argument might be feasible, but it is another breaking change, any way.

As I mentioned in the Motivation, non-affinity between NNBD and ? of current conditional expression is the cause of problems, but not Set literal, and it is the reason why I rename this issue to "deprecating ternary operator ?/:".

if(a : b : c) as conditional expression can be discussed without worry about the Set literal problem.

@Cat-sushi
Copy link
Author

Cat-sushi commented Dec 17, 2019

@lrhn

One design rule is that similar things should look similar, different things should look different.

Generally speaking, I totally agree with you.
That said, in context of replacing a ? b : c in existing code, syntactical clean break from if-statements and collection-ifs might be meaningful, while if(a : b : c) is resembles if-statements and collection-ifs slightly better than a ? b : c.

@tensor-programming
Copy link

tensor-programming commented Dec 19, 2019

Just thought I'd give some thoughts from the perspective of an educator.

A ton of languages have the ternary operator with the same syntax as in dart; cond ? a : b. C/C++, JavaScript, Python, Ruby, Java, Crystal etc. Having a familiar syntax makes it much easier to read the intention of the code if you are coming from another language. While perhaps something like if(a: b: c) might be a bit cleaner, it also may be a bad idea to split away from the established convention.

There are a few other alternatives from other languages that might be worth exploring. MySQL uses the IF(cond, a, b); syntax. R has an ifelse function which can be invoked like so ifelse (cond, a, b). Smalltalk uses ifTrue and ifFalse, aka cond ifTrue:a ifFalse:b. And many other languages allow you to write something along the lines of var x = if cond a else b with some using := instead of a simple =. Some of these could be implemented by the user as a function/method/extension method while others would be a bit harder to implement without modifying the VM.

@Cat-sushi
Copy link
Author

@tensor-programming
I agree with you, but this issue depends on #376.
If the only reason why the language team can't adopt a?[b] as null aware subscription is the ambiguity of {a?[b]:c} weather Map literal {(a?[b]): c} or Set literal {a ? ([b]) : c}, then I'd like to push this issue. The ambiguity is tolerable for me myself, so if the language team decide to adopt a?[b] as null aware subscription, then I'd like to close this issue regardless of other relatively minor problems.
I mean, my oder of precedence is ...

  1. adopt a?[b] as null aware subscripting and keep ternary operator a?b:c.
  2. adopt a?[b] as null aware subscripting and deprecate ternary operator a?b:c.
  3. adopt a?.[b] as null aware subscripting and keep ternary operator a?b:c.
  4. adopt a?.[b] as null aware subscripting and deprecate ternary operator a?b:c.

@tensor-programming
Copy link

@Cat-sushi I do agree with you from the standpoint of #376. If the subscript syntax becomes at odds with the ternary then learning to use the subscript properly would pose a challenge for newer devs.

@roman-vanesyan
Copy link

roman-vanesyan commented Jan 10, 2020

@Cat-sushi it worth noting that ternary operator wouldn't removed from the Dart language immediately as it will break a lot of code. So this issue wouldn't help to resolve the problem a?[b], otherwise Dart team risking to break Dart in the same manner as it was done with Pytnon 2 and 3.

@mockturtl
Copy link

Null-aware subscripting is now resolved.

@Cat-sushi
Copy link
Author

@vanesyan Dart will introduce a migration tool for NNBD, and I'm proposing the migration tool converting ternary operations into new syntax.

@Cat-sushi
Copy link
Author

The language team has decided that subscripting will have form of a?[b]. #376
Now, as I mentioned, I'm closing this issue, regardless of relatively minor problems remained.
Feel free to reopen this, or to post another issue to discuss deprecating conditional ternary operator.
Thank you.

@lrhn lrhn changed the title Deprecate ternary operator ?/: Deprecate conditional operator ?/: Sep 14, 2020
@yanok
Copy link

yanok commented Jan 4, 2024

I see the issue is long closed. Just wanted to add that with a switch expression we don't even need to invent a new syntax.
c ? a : b is just switch (c) {true => a, false => b} which is a bit more verbose, but is arguably more readable for complex a and b. I've seen many examples where people got operator precedence wrong in complex ternary expressions, switch expression won't allow this. Can we reconsider deprecation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

7 participants