-
Notifications
You must be signed in to change notification settings - Fork 75
A pragmatic approach #52
Comments
@Mouvedia How your suggestion could help the parsing issue? Remind that the difficulty for the parser is to determine efficiently, when it encounters |
It's possible though. Anyhow @claudepache illustrated a case in this comment which would be really problematic. This proposal circumvents it as requested by you. tl;dr: |
Not quite, because we don't know if we're in a ternary until we've parsed the entire expression. users[id]?.address?.street ? 'street found' : 'street missing'
//-------------------------^
// We don't know we're in a ternary until we've parsed `users[id]?.address?.street`
// insignificant whitespace...
users[id]?.address?.street ?[ 'street found'] : 'street missing'
//--------------------------------------------^
// We don't know we're in a ternary until we've parsed `users[id]?.address?.street?['street found']` |
Why not simply require parens around the nullish expression when in the context of a ternary?
|
For the same reason. We can't decide if we're in a ternary until we've parsed the entire expression. In this case, it's just a parenthesized expression. |
@jridgewell did you read my proposal ? I specifically said that having it part of a conditional expression is illegal. Again, |
@claudepache since using
The problematic cases were the ones where an expression using one of |
@Mouvedia how can it be made illegal without parsing difficulty, is the question? You can't know it's inside a ternary til you find the |
@ljharb I am saying that those cases weren't the blockers. tl;dr: the real complexity would be to handle nesting of ternaries mixed with expressions containing |
I understand now. It's the parsing efficiency aspect that is really the hangup. Not that it isn't possible, just that anything that makes it possible slows down the parser. |
@jrista Not always. We can use two thread to parsing two potential branch parallelly. It won't necessarily be slow if multicore is available , just double CPU costs, and much memory (memory won't be double, because AST nodes can be shared in two branch). @Mouvedia
I'm not sure whether it's a good idea though it seems possible at least in theory. We may have a very big expression like: a?(
function f() {
// very big function
// ...
}):null
^-- until here, we know it is ternary But the function f node can be reused in two AST branch, so it may be not a big deal. Any thought? |
Browsers have blocked changes in the past because it would necessitate this sort of parallel parsing; I'm relatively confident it's a nonstarter. |
@ljharb I can see the complexity which all parser maintainers hate. Just want to make it clear so everyone could accept it 😣 Conclusion: |
If any sort of parallel parsing is impossible , the only possible way for @Mouvedia 's proposal is add some hint before the optional chaining part: a?.b?[c] // ok, because there is `?.` before trouble `?[]`
a ?? b?() // ok, because there is `??` before trouble `?()`
a?() // not ok, parser will alway try to parse it as ternary Which make So you can write: const x = ?a.b.c?[d]?() I know it's a crazy idea, and have many ergonomics problem. Just throw it for you 🤪 |
Note that optional chaining and conditional have different precedence, leading to different parse trees: x*a?[b]+c; // (x * a?[b]) + c
x*a?[b]+c:d; // (x * a) ? ([b] + c) : d |
While there may be numerous ways to "make it work", I think in addition to parser efficiency we also need to account for ease of access and familiarity. Requiring odd constructs to support optional chaining within the context of a ternary is, IMO, a worse option than using a slightly odd but accessible and consistent and ultimately familiar operator (i.e. the |
I think that we only have 2 viable path left:
TC39 recently voiced that |
Maybe not a helpful question, but how is C# getting around the parsing issue for https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator |
@lehni I discussed this with a friend familiar with C#, and the answer is that for the |
Yes, one of the root cause is [1, 2, 3];
array[index];
obj[prop];
[a, b, c] = iterable;
class X {
[a] = [b] // guess what this mean?
} |
I don’t understand how that’s a problem. We parse the entire expression, we see that it’s invalid due to nesting an unparenthesized ?-chaining expression inside a ternary, and we stop parsing with a syntax error. |
How do you “parse the entire expression” before knowing whether the token Concretely, if the two constructs had the same precedence, we could use the fancy but proven technique known as “cover grammar”. But it is not the case: a?[b]+c // interpreted as: (a?[b]) + c
a?[b]+c:d // interpreted as: a ? ([b] + c) : d. Mandatory for BC. To be clear: I conjecture that it is possible to invent a smart technique that disambiguates the two cases, and, importantly, that proves that at most one interpretation of |
Ah, I missed the comments about “parallel parsing”. Yeah, for this to work we’d need to basically start building two parse trees in an ambiguous partial expression, and can give up one of the two once we know which one we got. Or need to look ahead. Both probably no-gos 😞 |
This is not unprecedented. It's more a matter of priorities. |
What is the precedent? |
As a side note, I refute my conjecture of #52 (comment) above: a?[b]?[c]:d is ambiguous, as it has two possible interpretations: (a?[b]) ? [c] : d
a ? ([b]?[c]) : d Of course, the ambiguity can be sidestepped by forbidding the mix of ternary and optional chaining at the same level, as proposed in this very thread... |
Requiring parenthesis in the case of a ternary in the statement would not be unheard of or unprecedented. Take for example dectructuring: const { property } = someObject This works fine. However, trying to do the following generates a syntax error: let property Instead you need parentheses around the statement to get it to pass: ({ property} = someObject) |
@Whobeu requiring parentheses in new features is acceptable. Requiring parentheses in old features breaks backwards compatibility, and is thus a complete no-go for many reasons (e.g. no one will use a browser that doesn't work with their favorite website, so browser vendors will refuse to implement it). |
@Zarel I don’t understand your comment: optional chaining is a new feature. |
Maybe I misinterpreted? I thought Whobeu was talking about ternaries. |
This is a bit off-topic. I already proposed something that required parentheses whenever you used the bracket notation. This is not what this issue is about. I am willing to add a gotcha to the language if in return I get consistency. The alternatives that are currently being advanced are IMHO way worse. |
@Zarel In this thread, we are talking about mixing ternary and optional chaining at the same level. We will certainly not require parentheses if only ternaries are involved. |
@claudepache Thanks. I wasn’t clear that parentheses would only be related to the use of optional chaining and ternaries together. Certainly not for all ternary statements. Just like my example where assignments only require parentheses when used destructuring. A blanket requirement to use parentheses on assignments was not imposed. |
[…] I guess unless someone modify |
The consistency problems are well known since several years, and alternatives has been extensively discussed since more than one and a half year ago (see issue #5 for the first discussions). None of the proposed alternatives has gain consensus. At some point of time, we should stop discussing and take decisions. Yes, we all know that the current syntax is suboptimal in some ways. But we don’t think that it is fatal. |
About the solution proposed in this Issue itself. Although it resolves the problem of “consistency”, it creates in return other problems:
Whether those new problems are considered more or less severe than the original one depends of course of the sensitivity of each individual. But at least, it should be clear that the proposed solution imposes compromises that are not easy. |
Swift has a gotcha around ternaries as well. It's less radical than what I am proposing though. I think that in most cases you won't have very long multi nested ternaries that would, undoubtedly, cause a performance hit.
Even if it's for only one character, we are already having a "lookahead". |
?.
/?[]
/?()
.If my assessment is correct, what about making
?.
/?[]
/?()
illegal if found in a conditional expression. I could easily live with this:This would be invalid:
Using ternaries is not refrained upon but it's not recommended either, so that restriction would probably help improve the readability. It's not unlike not being able to use
arguments.callee
in strict mode, you just have to know it.Am I missing something? Is my reasoning flawed?
It seems to be the most elegant solution so far.
The text was updated successfully, but these errors were encountered: