Closed claudepache closed 5 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?
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).
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
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 ?&
.
@claudepache Would it be coupled with a ?|
proposal? If not is it planned?
What would be the use case?
What would be the use case?
That's already ??
.
Do we even need .
here? What does happen when it's just a?&b
?
@SaschaNaz we would need all of .
, []
, and ()
to differentiate between optional member access, optional bracket access, and optional function invocation.
@ljharb I think we can still differentiate them without the dot.
a ?& b
a ?& [ i ]
a ?& ( x )
@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.
PROS: "pedagogically better", "consistency"
CONS: ?.
is already supported by Babel 7 β
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
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.
@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.
x.y
, x[y]
, and x(y)
— because I want to support the three cases;??
or ?:
?
may suggest “something with null” or “something conditional”;?.
Now looking at few proposed syntaxes:
?.
?[
?(
— is almost perfect, but it doesn’t meet Criterion 2, which is very bad.?.
?.[
?.(
— which is the current state, fails at least Criterion 5. People reported that ?.[
and ?.(
, fail Criteria 6 and 8 for them.?&.
?&[
?&(
— is what is proposed in this Issue. It meets objectively Criteria 1 to 5, and it fits well Criteria 6 in my opinion.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)
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 ??.
/??[
/??(
.
Why do you rank uniform access as more important than ease of use? (learning, typing)
Uniform access directly correlates to ease of use.
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).
@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.
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.
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.
@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:
?.
for property access which is the most common use caseFWIW, 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.
If we use ?.
for property access, imo that permanently blocks bracket and call access since ?[
and ?(
won't work.
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?()).
How about something like this?
For optional chaining:
??.
, ??[
, ??(
Null coalescing could go from ??
to ?|
to accommodate.
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.
There is a bit of a compelling cross-proposal consistency there, to be sure.
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 .
I think ??. and ??[ etc. are probably the best all-round solution/compromise given the constraints.
I do also prefer ??*
, if it can be coordinated with the null-coalesce proposal.
I'm happy to update the null coalescing proposal if it helps reach consensus on syntax.
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.
I would still find useful an optional bracket property access to keep the symmetry between dot and bracket notations.
This is essential IMO.
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?
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.
Strongly agree with @claudepache's fear.
@claudepache check https://github.com/tc39/proposal-optional-chaining/issues/34#issuecomment-332649067
??: for null coalescing
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.
Option 1: ?&.
, ?&[
, ?&(
, and perhaps later ?&:
Option 2: ??.
, ??[
, ??(
, and perhaps later ??:
Option 3: ?.
and perhaps later ??
(no support for computed member expressions or function calls)
Option 4: ?.
, ?.[
, and perhaps later ??
or ??:
or ?|
(no support for function calls, support for ugly-but-comprehensible member expressions – "the compromise")
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.
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.
Locking an issue prevents emoji reactions, ftr.
With the most upvotes and the least downvotes, Option 2 seems it will be the clear winner.
if it can be coordinated with the null-coalesce proposal
That's the crux of the matter; Option 2 requires it.
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:
Pros
?&
token distinct from.
is pedagogically better: One can picture the?&
token checking whether the LHS is null/undefined and performing short-circuiting if applicable, then the subsequent.
,[
or(
token performing regular property access or function invocation.Cons
?.
in C#, Swift, CoffeeScript and Groovy, and&.
in Ruby.)About the choice of ?&
?
recalls that we are dealing with null/undefined, as this character is used in several languages for denoting an optional/nullable value or object.&
recalls that the short-circuiting condition resemble the one of the&&
operator (replacing “falsy” with “nullish”).?
: “check for null”;&
: “perform eventual short-circuiting”.