You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently a prefix operator operates on the entirety of a selector chain, because selectors/suffix operators have higher precedence that prefix operators.
So, if you want to do (-foo.bar).baz, the parentheses are necessary, and it still reads a little confusingly, so you might want to write (-(foo.bar)).baz.
If we allow prefix operators to be used before the identifiers of selectors, then you could write foo.-bar.baz. The - would bind to the next selector name (plus any invocation arguments), so foo.-bar(args).baz would be (-(foo.bar(args)).baz).
This can be advantageous for null-shortening selector chains, because foo?.-bar would not be easily expressible, it's something like foo == null ? null : -(foo.bar) (where foo is only evaluated once, as usual). It would let null shortening apply to prefix operators when those operators are written inline in the selector chain.
It would also allow inline await, because await is a prefix operator: foo.await bar().baz would be (await foo.bar()).baz, but again without the extra parentheses. Might be a fix for #25.
Prefix operators are -, !, ~, --, ++ and await. We could choose to make throw a prefix operator, but we probably shouldn't.
For increment/decrement, we'd be able to do foo.++bar.baz and foo.bar++.baz. (The latter is actually not possible now, we should consider changing that. Likely a symptom of e++ being defined as equivalent to e += 1, even though the former could and should, just be a suffix operator which only applies to assignable expressions.)
the feature has some short-comings. It doesn't work with index-selectors: foo-[x] and foo![x] already means something else. It needs the . to separate the operator from the previous expression.
(Users might be tempted to do extension Itself<T> on T { get it => this; } and the foo[bar].-it.baz.)
It might be slightly confusing when -bar.baz means -(this.bar.baz) but this.-bar.baz does not. Still, the this is applied to the bar, not the entire expression.
An argument against this feature is that it's unclear how far the prefix operator binds. Take:
I'd probably want it to be the second entry, treating .bar() as a single "selector", and only working on one following selector. That doesn't leave any good way to include the ! if bar() returns an int?. On the other hand, including the ! doesn't handle the case where bar() returns a type with a ~ operator returning a nullable result.
The implicit "next operation" is not well-defined, and there is no way to provide a delimiter.
A more safe approach is to make prefix operators available as suffix operators as well, in some way (say allowing .op as syntax for treating the operator as a method, then foo.bar.~().baz would be equivalent to (~foo.bar).baz).
The text was updated successfully, but these errors were encountered:
lrhn
added
the
feature
Proposed language feature that solves one or more problems
label
Sep 11, 2020
Currently a prefix operator operates on the entirety of a selector chain, because selectors/suffix operators have higher precedence that prefix operators.
So, if you want to do
(-foo.bar).baz
, the parentheses are necessary, and it still reads a little confusingly, so you might want to write(-(foo.bar)).baz
.If we allow prefix operators to be used before the identifiers of selectors, then you could write
foo.-bar.baz
. The-
would bind to the next selector name (plus any invocation arguments), sofoo.-bar(args).baz
would be(-(foo.bar(args)).baz)
.This can be advantageous for null-shortening selector chains, because
foo?.-bar
would not be easily expressible, it's something likefoo == null ? null : -(foo.bar)
(wherefoo
is only evaluated once, as usual). It would let null shortening apply to prefix operators when those operators are written inline in the selector chain.It would also allow inline
await
, becauseawait
is a prefix operator:foo.await bar().baz
would be(await foo.bar()).baz
, but again without the extra parentheses. Might be a fix for #25.Prefix operators are
-
,!
,~
,--
,++
andawait
. We could choose to makethrow
a prefix operator, but we probably shouldn't.For increment/decrement, we'd be able to do
foo.++bar.baz
andfoo.bar++.baz
. (The latter is actually not possible now, we should consider changing that. Likely a symptom ofe++
being defined as equivalent toe += 1
, even though the former could and should, just be a suffix operator which only applies to assignable expressions.)the feature has some short-comings. It doesn't work with index-selectors:
foo-[x]
andfoo![x]
already means something else. It needs the.
to separate the operator from the previous expression.(Users might be tempted to do
extension Itself<T> on T { get it => this; }
and thefoo[bar].-it.baz
.)It might be slightly confusing when
-bar.baz
means-(this.bar.baz)
butthis.-bar.baz
does not. Still, thethis
is applied to thebar
, not the entire expression.An argument against this feature is that it's unclear how far the prefix operator binds. Take:
What is this equivalent to:
I'd probably want it to be the second entry, treating
.bar()
as a single "selector", and only working on one following selector. That doesn't leave any good way to include the!
ifbar()
returns anint?
. On the other hand, including the!
doesn't handle the case wherebar()
returns a type with a~
operator returning a nullable result.The implicit "next operation" is not well-defined, and there is no way to provide a delimiter.
A more safe approach is to make prefix operators available as suffix operators as well, in some way (say allowing
.op
as syntax for treating the operator as a method, thenfoo.bar.~().baz
would be equivalent to(~foo.bar).baz
).The text was updated successfully, but these errors were encountered: