tc39 / proposal-optional-chaining

https://tc39.github.io/proposal-optional-chaining/
4.94k stars 75 forks source link

Syntax: Use ?& (or anything else that correct the inconsistency between a?.b and a?.[b]) #34

Closed claudepache closed 5 years ago

claudepache commented 6 years ago

After careful consideration (see in particular #5), I think that the best syntax satisfying the difficult constraints we face (strong BC, consistency) is the following:

a ?& . b    // instead of   a ?. b
a ?& [ i ]  // instead of   a ?. [ i ]
a ?& ( x )  // instead of   a ?. ( x )

Pros

Cons

About the choice of ?&

madskonradsen commented 6 years ago

Sounds like a well thought through proposal. I believe the syntax is much more readable than the initial proposed ?. syntax(which just seemed wrong when being followed by ( or [ ). Is ?? totally off the table because of the nullary coalescing proposal?

samuelgoto commented 6 years ago

I think this is the right direction too (I agree that the consistency argument is a solid one).

Can you walk us through the alternatives considered? In addition to ?? what else could be explored and why ?& is favorited? I saw (?) proposed earlier and for documenting sake it would be good to state its trade-offs (e.g. makes it confusing for humans with a function call).

I'm curious about ?? too, sounds like if we should be optimizing for something it should be reserving it for optional chaining rather than nullary coalescing (i.e. i'd expect to see way more ??. accessors than ?? nullary coalescing).

tvald commented 6 years ago

Can you walk us through the alternatives considered?

There's a lot of discussion over in #5 about this, just search the page for ?&. Here's a short list of proposals from that thread:

?. - https://github.com/tc39/proposal-optional-chaining/issues/5#issue-237895428 ?? - https://github.com/tc39/proposal-optional-chaining/issues/5#issuecomment-320423660 ?! - https://github.com/tc39/proposal-optional-chaining/issues/5#issuecomment-320788203 ?: - https://github.com/tc39/proposal-optional-chaining/issues/5#issuecomment-320791656 |? - https://github.com/tc39/proposal-optional-chaining/issues/5#issuecomment-320791656 ?& - https://github.com/tc39/proposal-optional-chaining/issues/5#issuecomment-321024209 ?| - https://github.com/tc39/proposal-optional-chaining/issues/5#issuecomment-321024209

claudepache commented 6 years ago

I'm curious about ?? too, sounds like if we should be optimizing for something it should be reserving it for optional chaining rather than nullary coalescing (i.e. i'd expect to see way more ??. accessors than ?? nullary coalescing).

The main issue with ??, is that several other languages uses it for null-coalescing, which is a different feature. There is no possible confusion with ?&.

Mouvedia commented 6 years ago

@claudepache Would it be coupled with a ?| proposal? If not is it planned?

ljharb commented 6 years ago

What would be the use case?

Mouvedia commented 6 years ago

What would be the use case?

null coalescing operator

ljharb commented 6 years ago

That's already ??.

saschanaz commented 6 years ago

Do we even need . here? What does happen when it's just a?&b?

ljharb commented 6 years ago

@SaschaNaz we would need all of ., [], and () to differentiate between optional member access, optional bracket access, and optional function invocation.

saschanaz commented 6 years ago

@ljharb I think we can still differentiate them without the dot.

a ?& b
a ?& [ i ]
a ?& ( x )
ljharb commented 6 years ago

@SaschaNaz that wouldn't be consistent; that way member access would be special and magic, and there'd be no explicit indication that it was using member access.

Mouvedia commented 6 years ago

PROS: "pedagogically better", "consistency" CONS: ?. is already supported by Babel 7 β

Mathspy commented 6 years ago

CONS: ?. is already supported by Babel 7 β

A major version bump to babel-plugin-transform-optional-chaining solves that. Now plugins don't all share same version with Babel afterall

levithomason commented 6 years ago

Yep, I think this is the best synthesis of all the proposals and issues raised thus far. Nicely done.

If you're doubting the syntax, please 1) review all the linked material 2) get a feel by writing some fake code with it and 3) imagine syntax highlighting. After doing this, I'm pretty convinced this is a done deal.

claudepache commented 6 years ago

@samuelgoto

Can you walk us through the alternatives considered? In addition to ?? what else could be explored and why ?& is favorited? I saw (?) proposed earlier and for documenting sake it would be good to state its trade-offs (e.g. makes it confusing for humans with a function call).

I’m not going to review in detail all suggestions I have seen (they are numerous); but I’ll try to give some criteria for comparing their merits: the ideal but impossible syntax is the one that meets all criteria.

Here they are, approximately ordered by importance. Naturally, the real ordering will depend on your sensibility.

  1. backward compatible (i.e., something that does not have currently another meaning, or at least that is so obscure that it is practically unused) — because this is a strong requirement for web browsers for reasons;
  2. reasonably easily parsable (e.g., not something that requires infinite lookahead);
  3. works for static and dynamic property accesses, and function calls, i.e. x.y, x[y], and x(y) — because I want to support the three cases;
  4. not used in mainstream languages for other purpose, e.g.: ?? or ?:
  5. uniform across the three syntactical cases;
  6. suggestive — for instance the symbol ? may suggest “something with null” or “something conditional”;
  7. has precedent in other languages, typically: ?.
  8. beautiful;
  9. easy to type.

Now looking at few proposed syntaxes:

damieng commented 6 years ago

Let's not underestimate the ongoing human cost of a new and different syntax.

?. is well known across languages for safe property access. People coming to the language will have to learn a new syntax and will probably get frustrated and go read threads like this to try and understand why. It adds up.

Is uniform access across three syntatical cases more important than precedent in other languages? (it's more about familiarity and being able to jump back and forth between languages).

I think either ?. ?.[ ?.( or ?. ?&[ ?&( would be better than ?&. ?&[ ?&(

Try visually scanning and typing the following two lines a few times to see how your eyes and fingers feel about the proposal;

customer?.address?.zip customer?&.address?&.zip

(Typing ?. is incredibly easy on many layouts as the keys are adjacent. Throwing a far-away & between these two characters is painful)

tvald commented 6 years ago

People coming to the language will have to learn a new syntax

Compared to other language quirks like truthy/falsy and ASI, the burden of learning a slightly different operator seems negligible.

I would rank uniform access as one of the most important qualities for this proposal, just below the parsing / compatibility requirements which rule out ?./?[/?( and ??./??[/??(.

damieng commented 6 years ago

Why do you rank uniform access as more important than ease of use? (learning, typing)

ljharb commented 6 years ago

Uniform access directly correlates to ease of use.

jridgewell commented 6 years ago

Why do you rank uniform access as more important than ease of use?

Uniform operators are inherent in ease of use. I don't have to think "is it ?&. or ?., or ?&[ or ?[".

and ??./??[/??(

Null coalescing isn't an accepted proposal yet, so ?? is still open. The proposed ?& actually makes the operators different, so that might need to be address (making ?? free again).

claudepache commented 6 years ago

@damieng While I understand some of your sentiments, I think that the following argument is not valid:

Is uniform access across three syntatical cases more important than precedent in other languages? (it's more about familiarity and being able to jump back and forth between languages).

I think either ?. ?.[ ?.( or ?. ?&[ ?&( would be better than ?&. ?&[ ?&(

It is strictly less worse to have two different syntaxes in two different languages, than two different syntaxes in the same language. In that regard, ?. ?&[ ?&( is a non-starter.

meandmycode commented 6 years ago

I think it's come to a point where there is no great solution, it would be good to get an off the cuff idea of what developers are willing to concede to though (perhaps via a targeted twitter poll), one thing with customer?&.address?&.zip for me personally is that the amount of characters between the identifiers has started to pass the immediately obvious que's I'm used to seeing when traversing hierarchy.

And I feel the ampersand in there actually compounds that glanceable confidence, as historically the ampersand is used with joining individual statements, so at a glance I'm getting mixed messages of:

customer?.address?.zip and customer&&address&&zip

And it takes that bit longer to realise, I would hope that with better syntax highlighting and time this proves to be a a non-issue.

damieng commented 6 years ago

Uniform access directly correlates to ease of use.

That's not true on it's own. It aids predictability but syntax can be both uniform and yet not easy to use if it's not short and accessible.

I would argue ?&. is not accessible. It's hard to both read and type nor is it used anywhere in code or written language as far as I know.

Maybe it's just my hands hating the sequence (neither left nor right want's to drop & between two keys next to each other). I also find myself typing &?. for whatever reason. It's possible that this is just me but it's also possible that this is going to affect millions of JavaScript developers.

samuelgoto commented 6 years ago

@claudepache 's bullet points are a great framework, thanks for putting that together. I don't think, however, ?& is necessarily the best trade-off between the weights/ranking of the bullet points.

I mentioned this live in the meeting, but just wanted to state here my intuition (I'll link to the notes too).

works for static and dynamic property accesses, and function calls, i.e. x.y, x[y], and x(y) — because I want to support the three cases;

I don't think I generally rank criteria [3, 5] as high as @claudepache does.

Like it was pointed out earlier, property access is by far the most common use case compared to ()-calls and []-access (matches my intuition and the data that was collected too).

Given that there are no "perfect" solutions (e.g. ?., ?[] and ?()), I think it is a better trade-off to make the common use case super neat (a?.b) at the cost of not enabling (and cornering) the others (a?[b] and a?()).

I propose we:

FWIW, this was proposed somewhere by somebody else too, I'll link it here. FWIW2, waldemar seems to have brought up that this creates inconsistencies/incoherences, which weren't clear to the group and he is going to follow up in writing somewhere and I can report back here too.

ljharb commented 6 years ago

If we use ?. for property access, imo that permanently blocks bracket and call access since ?[ and ?( won't work.

samuelgoto commented 6 years ago

that's correct and that's the trade-off that i think is worth taking to enable the more common form a?.b for property access.

see earlier in my response:

at the cost of not enabling (and cornering) the others (a?[b] and a?()).

gisenberg commented 6 years ago

How about something like this?

For optional chaining: ??., ??[, ??(

Null coalescing could go from ?? to ?| to accommodate.

jridgewell commented 6 years ago

For optional chaining: ??., ??[, ??( Null coalescing could go from ?? to ?| to accommodate.

I also proposed something similar: ??., ??[, and ??(, with ??: for null coalescing, which keeps the base operator the same for all 4 cases.

ljharb commented 6 years ago

There is a bit of a compelling cross-proposal consistency there, to be sure.

samuelgoto commented 6 years ago

That sounds like the right trade off too and was my second choice. We pay a bit the price of the common case but everything else fits well, works for me.

(sent from phone, apologies for the brevity)

On Sep 27, 2017 4:54 PM, "Jordan Harband" notifications@github.com wrote:

There is a bit of a compelling cross-proposal consistency there, to be sure.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/tc39/proposal-optional-chaining/issues/34#issuecomment-332652199, or mute the thread https://github.com/notifications/unsubscribe-auth/AAqV6hw355SXkJVeBVKoJEV5Ii4iC-iXks5smrYUgaJpZM4PhH0r .

damieng commented 6 years ago

I think ??. and ??[ etc. are probably the best all-round solution/compromise given the constraints.

tvald commented 6 years ago

I do also prefer ??*, if it can be coordinated with the null-coalesce proposal.

gisenberg commented 6 years ago

I'm happy to update the null coalescing proposal if it helps reach consensus on syntax.

claudiopro commented 6 years ago

I initially found myself in disagreement with the proposal by @samuelgoto to exclude bracket access and function calls, but then I realized it makes sense in case of function calls, as it is a fundamentally different concept. I would still find useful an optional bracket property access to keep the symmetry between dot and bracket notations.

In that perspective, @jridgewell's solution sounds the most elegant to cover both cases and function calls too.

Mouvedia commented 6 years ago

I would still find useful an optional bracket property access to keep the symmetry between dot and bracket notations.

This is essential IMO.

samuelgoto commented 6 years ago

Chatting face to face with waldemar he brought up an interesting option: use ?? for both optional chaining AND the nullary coalescing operator. The trick is to define four tokens: ??, ??., ??[ and ??( at the same time such that lexers can pick the longest option.

That would enable something along the lines of:

let a = b??.c.d.e.f??(1, 2).g.h??["hello"] ?? "default value";

It is important to define them at the same time though, because you wouldn't be able to define ??[ after you get ?? defined in a backwards compatible way:

// If you push "??" first without the other tokens this means (b == null ? [1] : b)
let a = b ?? [1];

// meaning that now you can't now change the meaning to be of an optional
// chaining property access:
// (b == null ? undefined : b[1])

So, as long as you define all of these four tokens at the same time, this could work. Seems like an interesting proposal. WDYT?

claudepache commented 6 years ago

use ?? for both optional chaining AND the nullary coalescing operator. The trick is to define four tokens: ??, ??., ??[ and ??( at the same time such that lexers can pick the longest option.

I fear that it will cause confusion if a??[b] and a?? [b] are both valid with two wildly different meanings, as one could easily write the former when meaning the latter.

syg commented 6 years ago

Strongly agree with @claudepache's fear.

Mouvedia commented 6 years ago

@claudepache check https://github.com/tc39/proposal-optional-chaining/issues/34#issuecomment-332649067

??: for null coalescing

rattrayalex commented 6 years ago

I mostly want to 👏 👏 👏 the amount of thought going into this. @jridgewell , @claudepache , and @ljharb in particular. Everything on the table seems reasonable to me.

I think the "keyboard distance" argument against ?& warrants some attention. I might urge the proposers try the following test: time how quickly you can optionally-chain foo?&.bar?&[baz]?&.qux?&() vs foo??.bar??[baz]??.qux??() in your editor of choice.

(FWIW I got a whopping 20sec and 12sec, respectively – how embarrassing).

I might also urge the gutcheck of "convert a >100loc file you've used recently to use each style", a practice I have found immeasurably useful in designing syntax for LightScript.

I see four paths forward mentioned above that seem possibly palatable. In the past, I've found it useful to post each as a 👍 / 👎 poll, so I'll do that here; I hope it's constructive.

rattrayalex commented 6 years ago

Option 1: ?&., ?&[, ?&(, and perhaps later ?&:

rattrayalex commented 6 years ago

Option 2: ??., ??[, ??(, and perhaps later ??:

rattrayalex commented 6 years ago

Option 3: ?. and perhaps later ?? (no support for computed member expressions or function calls)

rattrayalex commented 6 years ago

Option 4: ?., ?.[, and perhaps later ?? or ??: or ?| (no support for function calls, support for ugly-but-comprehensible member expressions – "the compromise")

claudepache commented 6 years ago

Just a quick note:

Like some other people, I think it is essential to have support for both static (a?.b) and dynamic (a?.[x]) property access, whatever the proportion of use cases is. The reason is the orthogonality of features, in our cases “property access” and “optional chaining”.

When writing code, one uses sometimes static and sometimes dynamic access, and one may change between one style and the other in course of refactoring. Compare:

address.street;
address.city;

vs

for (let key of ['street', 'city']) {
    address[key];
}

and

foo.__iterator__

vs

foo[Symbol.iterator]

In those cases, it would be unwelcome to have to do a deeper and more cumbersome refactoring if the property accesses is optional.


I won’t argue about optional function call now (it is more properly to be discussed in, e.g., Issue #4), as a syntax for dynamic property access will most probably work for function call. I am of the opinion that we do not bind the choice of syntax with the decision about inclusion of optional function call.

levithomason commented 6 years ago

I would also vote we lock this issue and go with @rattrayalex's survey approach as the mechanism for expressing opinions moving forward. I haven't seen any new proposals or information presented on this issue (or the linked issues) in quite some time.

I'd even go so far as to say the README should be pointed to this survey. Users can read the material already posted for a very deep history and lively debate on the options presented.

ljharb commented 6 years ago

Locking an issue prevents emoji reactions, ftr.

levithomason commented 6 years ago

With the most upvotes and the least downvotes, Option 2 seems it will be the clear winner.

Mouvedia commented 6 years ago

if it can be coordinated with the null-coalesce proposal

That's the crux of the matter; Option 2 requires it.