Open subzero911 opened 7 months ago
Does this solves your problem?
var x = switch ((a, b)) {
(> 0, _) => 1,
(_, < 10) => 2,
_ => 3,
};
Does this solves your problem?
var x = switch ((a, b)) { (> 0, _) => 1, (_, < 10) => 2, _ => 3, };
It looks very non-intuitive.
At least, there are if-expressions in Rust, Python, Kotlin and Swift, along with switch/match expressions: https://doc.rust-lang.org/reference/expressions/if-expr.html https://www.hackingwithswift.com/swift/5.9/if-switch-expressions https://kotlinlang.org/docs/control-flow.html https://note.nkmk.me/en/python-if-conditional-expressions/
@subzero911, it looks like you are asking for a way to write statements in an if
expression. You could then use an immediately invoked function expression ("iife"):
var x = (){ if (a > 0) return 1; else return 2; }();
This would allow you to write arbitrary statements and deliver the result using return e;
, because you are now simply using a normal if
statement. (I don't know if this is what you mean by
Currently we have only
x ? y : z
operator, but it's not enough if we want more advanced branching.
because there's no branching that you can't express using the ?:
operator and parentheses, but it is true that you can't write statements in a ?:
expression.)
In any case, you might very well want to preserve the context type, because this can affect the meaning of a statement like return e;
. You could then use a helper function, as described in this comment:
X iife<X>(X Function() f) => f();
double x = iife((){ if (a > 0) return 1; else return 2; });
This causes 1
and 2
to become "integer literals with static type double
", which makes them work exactly like 1.0
and 2.0
. If you use the form (){...}()
then you won't have the context type double
at each return
statement, and the initialization of x
will then be a compile-time error.
You can do everything with a conditional expression, ?
/:
, today that you would be able to do with if
syntax, other than possilby not having an else
branch. Which doesn't make sense for expressions, so likely not even that.
The original example can be written as:
var x = (a > 0) ? 1 : (b < 10) ? 2 : 3;
Shorter. More like random line noise. But just as powerful. There is no "more advanced branching" when all you do is binary branches.
Switches can do more advanced branching.
That said, I'd be happy to allow an if
-expression (like we have an if
-element), just for the more verbose syntax.
There is an issue for that, https://github.com/dart-lang/language/issues/2306
var x = (a > 0) ? 1 : (b < 10) ? 2 : 3;
This is a so-called "ternary hell", rather an example of "how you shouldn't do". I mentioned a ternary operator just to let you know that I'm aware of it.
@subzero911 Do you think var x = if (a > 0) 1 else if (b < 10) 2 else 3;
is any better? Maybe we can solve the "ternary hell" by introducing the if-expression hell. I would rather have a > 0 ? 1 : (b < 10 ? 2 : 3)
.
How about this?
var x = switch (null) {
_ when a > 0 => 1,
_ when b < 10 => 2,
_ => 3,
};
@subzero911 Do you think
var x = if (a > 0) 1 else if (b < 10) 2 else 3;
is any better? Maybe we can solve the "ternary hell" by introducing the if-expression hell. I would rather havea > 0 ? 1 : (b < 10 ? 2 : 3)
.
Agreed, var x = if (a > 0) 1 else if (b < 10) 2 else 3;
looks like a hell. Just add curly braces, and it would become readable (there's a lint suggesting to always use curly braces).
How about this?
var x = switch (null) { _ when a > 0 => 1, _ when b < 10 => 2, _ => 3, };
Yeah, we have when
clauses, I thought about it too.
@subzero911, it looks like you are asking for a way to write statements in an
if
expression. You could then use an immediately invoked function expression ("iife"):var x = (){ if (a > 0) return 1; else return 2; }();
var x = if (a > 0) { return 1; }
is still more concise than () {}()
syntax.
Also, it's easy to miss the last ()
while reading.
I personally don't like bringing every nice feature from other popular languages.
Reason why Rust/Kotlin/Python/Swift
have such expressions - they were designed without ternary operator.
But if they had ternary operator for historical reasons, they wouldn't introduce a new way of doing the same thing with very little gain.
IIFE looks good to me - I use it all the time with switch expression, however I always end up with extracting them to separate functions - for readability and documentation purposes.
However, if Dart didn't choose to proceed with Pattern Matching and, instead, stuck to flow analysis, having if expression (instead of switch expressions) would be justifiable.
May just be personal preference, but ternary expressions have been driving me absolutely mad. I desperately want an if...else expression so that I can completely avoid ternaries.
Here are my arguments in favor of an if...else expression:
return
if (predA) 'A'
else if (predB) 'B'
else if (predC) 'C'
else null;
<keyword> <expression>
syntax used everywhere else in dart, including in if statements:
if (<predicate) <expression> ...
(<predicate>) ? <expression> ... // <--- order is flipped, throws me off every time
My spicy-hot take is that ternaries are a dated syntax that doesn't make a lot of sense in a modern language.
Here are my thoughts on all the previous points made so far:
if...else
expressions would be nice, but is a whole other can of worms so it probably shouldn't be considered at least for now. See:
https://github.com/dart-lang/language/issues/132
https://github.com/dart-lang/language/issues/3117There is a ternary expression x ? y : z
and kind of unary null ??=
guard. It would be also nice to have an unary conditional safeguard. expression.
Not sure about the syntax, but instead of something like this:
x = condition ? y : null
the thought is to avoid the "null" part and write something like:
x = if(condition) y
In a Flutter context it is commonly used for gesture callback, e.g. onTap
, etc (convention a null callback makes widget disabled).
The example looks like it can just be
x = condition ? y : null;
or if assigning null
is a no-op:
if (condition) x = y;
Saving the : null
isn't that many characters (every reasonable syntax will have at least as many characters as
?
/:
), and it's makes the code less explicit. An implicit null
isn't as readable as an explicit one.
It's also possible to implement this as an extension method.
extension GuardOrNull on bool {
T? call<T>(T value) => this ? value : null;
}
Which you can use as:
x = (condition)(x);
Biggest issue is that it doesn't promote.
I think a new syntax is more important for conditionally passing arguments, like
foo(1, x: if (o != null) o.length)
so that book argument is passed if the condition is false.
The example looks like it can just be
x = condition ? y : null;
or if assigning
null
is a no-op:if (condition) x = y;
Saving the
: null
isn't that many characters (every reasonable syntax will have at least as many characters as?
/:
), and it's makes the code less explicit. An implicitnull
isn't as readable as an explicit one.
In your last assessment you don't take into account readability when y
expression is long (sometimes several lines long).
And your example with an if statement can't be used when you need to return a value. Eg. Flutter reference in my comment:
onTap: if(buttonIsEnabled && notTheLastEntry) _onDelete,
Instead of extension on a boolean we could use a static method to be used like:
onTap: ifTrue(buttonIsEnabled && notTheLastEntry, _onDelete),
Though with an inline function declaration that will require an additional lambda, comparing with ternary or potential unary conditional expression.
You already introduced switch expressions. Are you planning to add if expressions?
Syntax examples:
Currently we have only
x ? y : z
operator, but it's not enough if we want more advanced branching.