Closed
Listed in
Description
If we simply make !
a postfix operator using the existing grammar, I believe we get the behavior that await e!
is parsed as await (e!)
. Since await
is accepting of null
, it seems like it would be more useful to make it bind more tightly than !
, and hence treat await e!
as (await e)!
. I'm not sure whether this matches the users intuition or not though. Would it be surprising to the user if await x++
were parsed as await (x++)
but await x!
were parsed as (await x)!
?
Thoughts?
Issue raised by proposed tests here.
cc @lrhn @munificent @eernstg @stereotype441 @danrubel @bwilkerson @scheglov
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
danrubel commentedon Apr 17, 2019
If we make this change, then does that mean that
would be parsed as
?
If yes, then yes I think this change will confuse users
stereotype441 commentedon Apr 17, 2019
I keep coming up with reasons to prefer that we make
!
a postfix operator using the existing grammar (and thereforeawait e!
should parse asawait (e!)
). Here's what I've been thinking:++
,--
, member access, method call, and indexing, in that all of them kind of look like suffixes that attach to the right hand side of an expression, and they all have maximum precedence.!
feels like it belongs in the same category, especially given how often we expect users to use it in conjunction with the other suffixes. I guess this argument is similar to @danrubel's.!
to bind extremely tightly, because there's practically nothing useful one can do with a nullable value; any time an expression contains both!
and some other operator, the user probably wants the!
to take effect first.await
there's any reason to prefer(await e)!
overawait (e!)
. Both parses have legitimate use cases.await e
when I want to do operations on the result of the await. So having to explicitly parenthesize to get(await e)!
is unsurprising. But I think I'm going to quickly get in the habit of assuming that I never need parentheses arounde!
, so having to explicitly parenthesize to getawait (e!)
would be a surprise.!
, what do we lower it to? Do we make it bind less tightly than unary prefix operations? Because that seems bad--it would make-x!
parse as(-x)!
; the current parse of-(x!)
is much better.leafpetersen commentedon Apr 17, 2019
I think the two parsing concerns can be resolved;
For
var x = await foo!.bar();
you'd need to split oute!
from the unary expressions, something like:This doesn't resolve the
-x!
problem, but I think (but could be wrong) that you can do the same thing here: split unaryExpression again, so that applying!
directly to a prefix operator application isn't a valid parse.To the rest of the concerns:
Fair point.
I think this is generally true, but not for
await
.is valid code if you parse as
(await x)!
but rejected if you parse asawait (x!)
.In general,
await
is already null aware, so projecting out of nullability world doesn't do much for you.I guess the example you have in mind is something like this?
Fair enough.
Good point.
munificent commentedon Apr 17, 2019
I'm with Paul. Let's keep it simple.
In general, having precedence at all is sort of a "hack". It lets users omit explicit parentheses when composing some expressions, at the expense of making the actual order of evaluation completely hidden in the source text.
Unless you've memorized the precedence table, you simply don't know how this will evaluate:
Any syntax feature that relies on material already being in the reader's head should be approached with caution. Keywords are somewhat OK because there's at least a word the reader can maybe infer some semantics from. If I've never seen
await foo
before, I can at least guess that something related to time and pausing is involved. Operators are worse because you can't "read" punctuation. No English dictionary is going to help you guess at what, say,..
does. Precedence is possibly the worst of all because there is no source text at all to look at. You're trying to understand what the absence of explicit grouping characters means.Thus, I think we should try very very hard to stick to well-established precedence rules and avoid "innovating". We already got burned really badly with cascades.
Parentheses aren't intrinsically bad. They make the code a little more verbose, but they make the precedence explicit, which is good for any reader who doesn't know the precedence. So, in this case, I think keeping the familiar precedence of other unspaced-postfix operators (
++
,--
,.
,[...]
,?.
) is a good thing. The goal isn't to maximize brevity, but clarity.leafpetersen commentedon Apr 17, 2019
This all seems reasonable to me. Thanks for the discussion! Closing this, resolving in favor of treating this uniformly.