Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

A pragmatic approach #52

Closed
Mouvedia opened this issue Mar 7, 2018 · 39 comments
Closed

A pragmatic approach #52

Mouvedia opened this issue Mar 7, 2018 · 39 comments
Labels
alternative syntax past ideas and discussions about alternative syntaxes

Comments

@Mouvedia
Copy link

Mouvedia commented Mar 7, 2018

  1. Everyone would welcome having the triptych ?./?[]/?().
  2. We can't have it due to conflicts with the conditional/ternary operator.

If my assessment is correct, what about making ?./?[]/?() illegal if found in a conditional expression. I could easily live with this:

let street = users[id]?.address?.street;

console.log(street ? 'street found' : 'street missing');

This would be invalid:

// throws (syntax error?)
console.log(users[id]?.address?.street ? 'street found' : 'street missing');

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.

@claudepache
Copy link
Collaborator

claudepache commented Mar 7, 2018

@Mouvedia How your suggestion could help the parsing issue?

Remind that the difficulty for the parser is to determine efficiently, when it encounters ?[ or ?(, whether it will be optional chaining or conditional. Until it knows that, any restriction around conditional operator in presence of optional chaining (and vice versa) cannot be enforced, and therefore cannot help.

@Mouvedia
Copy link
Author

Mouvedia commented Mar 7, 2018

Remind that the difficulty for the parser is to determine efficiently

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: a?[b]:c would require to introduce some kind of operator precedence as explained by pronebird.

@jridgewell
Copy link
Member

This proposal circumvents it as requested by you.

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']`

@jrista
Copy link

jrista commented Mar 7, 2018

Why not simply require parens around the nullish expression when in the context of a ternary?

(users[id]?.address?.street) ? 'street found' : 'street missing'

@jridgewell
Copy link
Member

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.

@claudepache
Copy link
Collaborator

@Mouvedia @jrista

I think at least one of us is missing something. What problem exactly are you trying to solve?

Consider the two statements:

a?[b]+c;      // interpreted as:  a?[b] + c;
a?[b]+c:d;    // interpreted as:  a ? [b]+c : d;

How your suggestion would help to parse the two cases more efficiently?

@Mouvedia
Copy link
Author

Mouvedia commented Mar 8, 2018

@jridgewell did you read my proposal ? I specifically said that having it part of a conditional expression is illegal.

Again, users[id]?.address?.street ? 'street found' : 'street missing' is not allowed and throws. That's the point of the proposal.

@Mouvedia
Copy link
Author

Mouvedia commented Mar 8, 2018

How your suggestion would help to parse the two cases more efficiently?

@claudepache since using ?./?[]/?() would not be allowed in a conditional expression:

  • a?[b]+c would require to hit a , or a ; to know we are not in a ternary and
  • a?[b]+c:d must be a ternary and it's known once it reaches the :.

The problematic cases were the ones where an expression using one of ?./?[]/?() was part of a conditional expression, that's when the parser would have had to add operator precedence or some complicate backtracking mechanisms.

@ljharb
Copy link
Member

ljharb commented Mar 8, 2018

@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 :. Engines don't want to have to maintain two potential parse trees just to navigate a potential ternary-or-optionalchain.

@Mouvedia
Copy link
Author

Mouvedia commented Mar 8, 2018

@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 ?./?[]/?().

@jrista
Copy link

jrista commented Mar 8, 2018

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.

@hax
Copy link
Member

hax commented Mar 9, 2018

@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
If I understand your proposal correct, you suggest we disallow optional chaining and ternary together in a expression so

a?[b]?[c]?(e, f)?.h()
                 ^-- here we know this expression will not have ternary, 
                     and we can just stop wasting CPU/memory on ternary branch
a?[b]?[c]?(e, f):g:h:i
                ^-- here we know this expression will not have optional chaining, 
                    and we can just stop wasting CPU/memory on optional chaining branch

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?

@ljharb
Copy link
Member

ljharb commented Mar 9, 2018

Browsers have blocked changes in the past because it would necessitate this sort of parallel parsing; I'm relatively confident it's a nonstarter.

@hax
Copy link
Member

hax commented Mar 9, 2018

@ljharb I can see the complexity which all parser maintainers hate. Just want to make it clear so everyone could accept it 😣

Conclusion:
It might be a pragmatic approach for JS programmers,
but it fails to be a pragmatic approach for the maintainers of JS parsers.

@hax
Copy link
Member

hax commented Mar 9, 2018

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 null ?? EXPRESSION_CONTAINS_OPTIONALCHAINING become a pattern.
And maybe we can shorten it to ?EXPRESSION_CONTAINS_OPTIONALCHAINING.

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 🤪

@claudepache
Copy link
Collaborator

claudepache commented Mar 9, 2018

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

@jrista
Copy link

jrista commented Mar 12, 2018

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 ?. currently proposed). With the currently proposed operator, it solves the problem being discussed here, and would be consistent in all use cases.

@Mouvedia
Copy link
Author

Mouvedia commented Nov 2, 2018

I think that we only have 2 viable path left:

  1. inefficient parsing
  2. concede that only ?. will be unanimously accepted

TC39 recently voiced that ?. cannot be introduced without its pendant—let's call it ?[] for now—which means 2. is not an option. It's totally understandable but Id rather have ?. than nothing.
We are left with 1.; are there any precedents of dangling paths in ECMAScript i.e. momentarily splitting the parse tree?

@lehni
Copy link

lehni commented Nov 2, 2018

Maybe not a helpful question, but how is C# getting around the parsing issue for ?.? It is syntactically quite similar to JS and has both ternary (conditional operator) and optional chaining (null-conditional operator):

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators

@Zarel
Copy link

Zarel commented Nov 2, 2018

@lehni I discussed this with a friend familiar with C#, and the answer is that for the ?( case, C# doesn't have optional call (nullable first-class functions seem somewhat obscure as a pattern), and for the ?[ case, C# doesn't use [1, 2, 3] as an array literal (it would be new [] {1, 2, 3}).

@hax
Copy link
Member

hax commented Nov 16, 2018

Yes, one of the root cause is [] is too overloaded in JS.

[1, 2, 3];
array[index];
obj[prop];
[a, b, c] = iterable;
class X {
  [a] = [b] // guess what this mean?
}

@flying-sheep
Copy link

we don't know if we're in a ternary until we've parsed the entire expression.

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.

@claudepache
Copy link
Collaborator

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?[ or ?( should be interpreted as part of optional chaining or ternary?

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 ?[ is possible without syntax error. But although it would be a very interesting theoretical exercise, I am not willing to inflict it to the parsers, and even less to the users, because a typo omitting or adding a colon (e.g., a?[b]+c;d) would have a non-local effect.

@flying-sheep
Copy link

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 😞

@Mouvedia
Copy link
Author

Mouvedia commented May 7, 2019

This is not unprecedented. It's more a matter of priorities.

@ljharb
Copy link
Member

ljharb commented May 7, 2019

What is the precedent?

@Mouvedia
Copy link
Author

Mouvedia commented May 7, 2019

@ljharb #59 (comment)

@claudepache
Copy link
Collaborator

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

@Whobeu
Copy link

Whobeu commented May 7, 2019

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
{ property} = someObject

Instead you need parentheses around the statement to get it to pass:

({ property} = someObject)

@Zarel
Copy link

Zarel commented May 9, 2019

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

@claudepache
Copy link
Collaborator

@Zarel I don’t understand your comment: optional chaining is a new feature.

@Zarel
Copy link

Zarel commented May 9, 2019

Maybe I misinterpreted? I thought Whobeu was talking about ternaries.

@Mouvedia
Copy link
Author

Mouvedia commented May 9, 2019

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.

@claudepache
Copy link
Collaborator

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

@Whobeu
Copy link

Whobeu commented May 9, 2019

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

@claudepache claudepache added the alternative syntax past ideas and discussions about alternative syntaxes label Jun 9, 2019
@Mouvedia
Copy link
Author

Mouvedia commented Jun 10, 2019

[…]

I guess unless someone modify spec.html we won't be taken seriously.
Closing all the alternatives as a batch is a really lame move.
This proposal has consistency problems and hiding the problems won't fix them.

@claudepache
Copy link
Collaborator

@Mouvedia

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.

@claudepache
Copy link
Collaborator

About the solution proposed in this Issue itself. Although it resolves the problem of “consistency”, it creates in return other problems:

  • It poses technical challenges for spec writers and maintainers. Here, the goal is not only to write the spec, but to prove that it is non-ambiguous.
  • The necessity of parallel parsing may impose unwelcome performance regressions to implementers (and in fine to users).
  • It disallows the mix of ternary and optional chaining without loading the expression with additional parentheses.

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.

@Mouvedia
Copy link
Author

It disallows the mix of ternary and optional chaining without loading the expression with additional parentheses.

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.

The necessity of parallel parsing may…

Even if it's for only one character, we are already having a "lookahead".
Anyway like I said, it needs to be specced else it won't even be considered.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
alternative syntax past ideas and discussions about alternative syntaxes
Projects
None yet
Development

No branches or pull requests

10 participants