-
Notifications
You must be signed in to change notification settings - Fork 214
Remove/redefine conditional expression syntax. #2306
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
Comments
I do occasionally see multiply-nested conditional expressions, which will get a few characters wider with this syntax. But there's an opportunity with the migration to upgrade to a case expression where applicable: void foo(MyEnum a) {
return a == MyEnum.one
? "one"
: a == MyEnum.two
? "two"
: a == MyEnum.three
? "three"
: "four";
}
// becomes
void foo(MyEnum a) {
return switch (a) {
case MyEnum.one => "one";
case MyEnum.two => "two";
case MyEnum.three => "three";
case MyEnum.four => "four";
};
} |
Would this allow for |
This has potential to make nested conditional expressions much more legible. Take this code for example, which was formatted using String grade(int scoreInPercent) => scoreInPercent >= 90
? 'A'
: scoreInPercent >= 80
? 'B'
: scoreInPercent >= 70
? 'C'
: scoreInPercent >= 60
? 'D'
: 'F'; With the String grade(int scoreInPercent) =>
if (scoreInPercent >= 90)
'A'
else if (scoreInPercent >= 80)
'B'
else if (scoreInPercent >= 70)
'C'
else if (scoreInPercent >= 60)
'D'
else
'F'; |
Collection-if puts us in a potentially confusing position. If we do var y = if (false) 1; // "null".
print([if (false) 1]); // "[]", *not* "[null]". Now imagine that we do what we've wanted for several years and allow arg([int? x = 1]) print(x);
arg(if (false) 2); Does this print Before we go in this direction, I think we should have a very clear model of when a value is absent versus when a value is filled in with A potentially larger problem is that if we give them |
I generally agree that defaulting to I think I'd prefer to require the |
Forgive me if I'm wrong, would this issue pave the way for something like this to be implemented? final result = if (foo) {
final value = _someMethod();
value + 4
} else {
final value = _someMethod();
value + 2
} |
I don't think so, but the new switch might do what you want. |
I love this proposal. I absolutely hate writing Why?
I'm not a fan of the suggested implicit null. It feels like it has a lot of potential footguns. I'd be for requiring exhaustivity or an implicit
If we imagine this proposal going through, it could also lay groundwork for a much more dramatic longterm shift towards everything-is-an-expression with multi-statement if-expression blocks which interplays nicely with multi statement switch bodies: // Before.
var foo = if (someBool) 1 else 2;
// After.
var foo = if (someBool) {
someSideEffect();
1;
} else {
2;
} A couple more changes and you'd have what I secretly wanted this whole time: Dart is just Scala without the baggage. All this kind of begs the question: why not just use a switch expression? And I'd argue that switch expressions have a very specific purpose: they're a restrictive construction designed to give the compiler enough information to barf if you haven't directly addressed each potential case. |
The current "ternary" conditional expression syntax,
e1 ? e2 : e3
, uses the?
character which also have several other uses in the Dart syntax (null-aware member access, nullable spread operators, nullable types, all null-related).It would be nice to use
?
for more null aware syntax, but it very easily conflicts with the?
/:
syntax.So, let's remove that syntax.
Since we still want expression-level conditionals, we can embrace what we are already doing in literals, and what we are suggesting doing with
switch
: Make the statement syntax also work as expression syntax.Proposal
That is, change
e1 ? e2 : e3
toif (e1) e2 else e3
as an expression. (Disclaimer: Cannot be used to start a statement expression or map/set literal element. Cannot omitelse
, or alternatively, can only omitelse
wherenull
is a valid value, with a default ofelse null
. Theelse
binds to nearest possibleif
in case of ambiguity. The usual.)Grammar
The grammar is simple, we put it where the
?
/:
expression currently is:with the provision that
Semantics
Same as
?
/:
, with the extra option of omitting theelse
branch, in which case it's implicitly the same aselse null
.Expressions always have a value.
Migration
We can automatically migrate all
e1 ? e2 : e3
toif (e1) e2 else e3
. We will have to do so.Pros
Not many by itself
Some people will likely find the
if
syntax easier to read than the?
/:
. Especially in contexts where?
and:
both mean something else as well.Allows omitting
else
. I have written far too many expression of the formx != null ? action(x) : null
. Usingif (x != null) action(x)
is actually four characters shorter than the?
/:
.Opens up the grammar to using
?
more for other null-aware operations! This is the goal!Cons
More verbose. The
?
/:
syntax is literally two characters, it's very hard to make shorter. Theif
syntax is eight characters ("if()else"). You save some whitespace in the beginning (if (e1) e2
vse1 ? e2
The same
if
syntax now exists in three different versions (expressions, elements and statements). That's consistent, but also makes it harder to recognize what's going on without further scrutiny.Allows forgetting
else
. That gives you a nullable value, which you may notice if the then expression wasn't nullable. Accidental mistakes can be hidden if the then branch was already nullable.Requires migration, even if it's automated.
Why?
Opening up the
?
to things.Consider null-aware operators:
x ?< 4
,x ?- 2
.Without the conditional expression in the mix, parsing this should be "more doable".
The text was updated successfully, but these errors were encountered: