Skip to content

Inline prefix operators #1216

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

Open
lrhn opened this issue Sep 11, 2020 · 0 comments
Open

Inline prefix operators #1216

lrhn opened this issue Sep 11, 2020 · 0 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@lrhn
Copy link
Member

lrhn commented 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), 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:

 foo.~bar()![1]

What is this equivalent to:

(~foo.bar)()![1] 
(~foo.bar())![1]
(~foo.bar()!)[1]
(~foo.bar()![1]) 

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

@lrhn lrhn added the feature Proposed language feature that solves one or more problems label Sep 11, 2020
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

1 participant