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

Dot after question mark #5

Closed
madskonradsen opened this issue Jun 22, 2017 · 78 comments
Closed

Dot after question mark #5

madskonradsen opened this issue Jun 22, 2017 · 78 comments
Labels
alternative syntax past ideas and discussions about alternative syntaxes

Comments

@madskonradsen
Copy link

I'm very very glad to see this become resurrected. One thing I'm curious about is why you chose to have a dot after the question mark in case of function invocation and why you have a dot between question marks and square bracket notation. It seems strange to me, but perhaps i'm too biased by having written too much CoffeeScript.

a?.()
a?.[b]
@claudepache
Copy link
Collaborator

The issue with a?() and a?[b] is that it is difficult to distinguish (both for humans and machines) from the ? : operator. See claudepache/es-optional-chaining#3

@madskonradsen
Copy link
Author

Sigh. Alrighty then. Closing up :)

@pronebird
Copy link

I think this should be reconsidered. Such expression is used in many languages already.

@madskonradsen
Copy link
Author

I'm reopening then. Some people(in other issues) have expressed interest in having this debate again...

@madskonradsen madskonradsen reopened this Jul 21, 2017
@claudepache
Copy link
Collaborator

The conceptors of C# did apparently face a similar parsing issue around ?(. From https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators:

You need to explicitly call the Invoke method because there is no null-conditional delegate invocation syntax PropertyChanged?(e). There were too many ambiguous parsing situations to allow it.

@pronebird
Copy link

pronebird commented Jul 22, 2017

@claudepache you're right about C#.

Although it works in Swift but they have their own constraints that make it feasible to disambiguate optional chaining from ternary expression, such as spaces are taken into account, i.e:

true?"whoa":"boo"; // error
true ?"whoa":"boo"; // ok
true ? "whoa" : "boo"; // ok, readable

It's possible to support the var?() or var?[] syntax by forcing the blank space before ? to disambiguate the intent:

a?()?[b]+c?.d:e?.f; // error
a?() ? [b]+c?.d : e?.f; // ok
a?() ?[b]+c?.d : e?.f; // ok, less readable
a?() ?[b]+c?.d:e?.f; // ok, unreadable

In case if there is no space before ? the error is most likely to follow due to orphaned : since ? falls into optional chaining.

To conclude my take on that:

  • a? is optional chaining
  • a ? is a part of expression with ternary operator.

After spending some time on that I must admit that ? is a breaking change and ?. is not. It's safer to stick to ?. for the sake of backward compatibility.

@Mouvedia
Copy link

Mouvedia commented Jul 22, 2017

Personally I would separate ? followed by a dot proposal—which is not controversial—from ? followed by [ or ( which, IMOH, should be breaking changes. The babel plugin should support ?. right away and this repository should strip anything related to things akin to ?[ and ?( so that new proposals for them may be championed.

@levithomason
Copy link

levithomason commented Aug 5, 2017

I think consistency is important.

IMHO, the . should strictly be reserved for property accessors only. A ? operator makes perfect sense in the a?.b?.c cases, however, to shim it into a ?. operator in a?.[] and a?.() I fear will bring a lot of confusion to the feature.

This also creates an inconsistency that I haven't seen mentioned. Given the operator ?.:

Case 1: a() becomes a?.()
Case 2: a[] becomes a?.[]
Case 3: a.b becomes a?.b - What happened to the dot property accessor?

The operator ?. has consumed the dot property accessor in Case 3. No other operators or notation in the language consume sibling syntax in this way. Inserting the operator directly into the original syntax would produce a?..b as a + ?. + .b.

It seems as if the original idea was to support a?.b and it was then shimmed into other cases as the ?. operator because there was a dot in the original use case. I don't think a?..b is a good idea, just as I don't think a?.() and a?.[] are good ideas.

The a?.b case is a great idea because it is saying "object a is optional, if available I'm going to access property b via the dot property accessor". However, this would then mean the operator is actually ? and not ?..

Proposals

Initially support a?.b only

In kind with @Mouvedia, I'd almost rather see initial support for the optional chaining operator in dot properties only and let the later proposals add the other cases. Similar to what was done with the proposed export extensions.

Always two character

If the proposal must support all use cases, then I think:

  • a dot should not be involved in the operator (confusing)
  • it must appear as two characters in all usages (consistent)
  • it should never consume sibling syntax (confusing)

This leads me to an operator like ??:

  • a??()
  • a??[]
  • a??.b??.c

This is not 100% ideal but it is at least consistent. Again, I'd go for the basic support only first though.


In any case ☮️ ❤️ , thanks

@claudepache
Copy link
Collaborator

A two-char operator is possible, yes. There is however a tension between keeping a better syntax for the common case (a?.b) and having an absolutely consistent syntax in all cases.

Anyway, although it is technically possible, we should avoid to use the same symbol ?? for both Optional chaining and Null coalescing, because it has opposite semantics:

  • null coalescing: a ?? b: evaluates tob if a is null;
  • optional chaining a?.b: evaluates to a.b if a is not null.

Possible alternative: a:?.ba:?[b]a:?(b)

@claudepache
Copy link
Collaborator

claudepache commented Aug 6, 2017

About the suggestion to initially support a?.b only.

I’m against it. According to statistics in #17, in a sample of CoffeeScript code, there are significant uses of a?(b) (12%) and a?[b] (5%), so it is important to support those cases as well. If we support a?.b only, syntax issues for other cases will not become more easily resolvable: those problems are known since long ago, probably several years, and no solution that satisfies everyone has been found yet.

It is best to decide now (I mean, during the next few months) for a syntax for all cases, whether it is uniform or slightly inconsistent with better look for the common case.

@Mouvedia
Copy link

Mouvedia commented Aug 6, 2017

initially support a?.b only

I am talking about the babel plugin. We shouldn't push stuff like ?.[] in the wild even at stage 1.

It is best to decide now … for a syntax for all cases

That's alright by me but the current proposal do favor certain syntaxes that are controversial and inconsistent with ?. as explained just above. IMHO the parser should have to struggle not us.

@lsim
Copy link

lsim commented Aug 7, 2017

I understand the unfortunate reasons why a?[..] isn't going to happen - but I'm with levithomason here. While a??.b isn't ideal, I think it is at least tolerable if it means we don't have to have a?.[foo]

In my opinion, a?.[foo] is pretty confusing as it is easy to accidentally read the ?. operator as two separate operators - especially for us poor souls that have written lots of coffeescript.

@claudepache
Copy link
Collaborator

claudepache commented Aug 7, 2017

In my opinion, a?.[foo] is pretty confusing as it is easy to accidentally read the ?. operator as two separate operators - especially for us poor souls that have written lots of coffeescript.

Well, as someone who as written lots of JavaScript, I find CoffeeScript semantics of ? quite confusing at first sight:

a ?[b]        # in JS: a != null ? a : [b]`
a?[b]         # a != null ? a[b] : undefined;

null ?[b]     # [b]
null?[b]      # syntax error
(null)?[b]    # undefined

a = { 'x': 42 }
a ?['x']      # a
a?['x']       # 42

Also:

42 // comment  # Math.floor(42/comment)
a ? b : c      # a != null ? { b: c } : undefined

I’m not saying that we should ignore coffeescripters’ habits. But if you switch between CoffeeScript and JavaScript, you must already know that there are many things that work differently.

@tvald
Copy link

tvald commented Aug 7, 2017

I like @levithomason's proposal for ??./??()/??[]. While slightly verbose, consistency across all the cases (member access, invocation, indexing) is important, and an opportunity to improve over the support in other languages.

The double-question-mark syntax also nicely parallels nullary-coalescing ??.

@claudepache
Copy link
Collaborator

claudepache commented Aug 7, 2017

The double-question-mark syntax also nicely parallels nullary-coalescing ??.

As I said above, for me it is a problem rather than an advantage, because the null-coalescing operator has an opposite meaning. (I know that CoffeeScript uses the same symbol for both operators, but CoffeeScript is somewhat confusing.)

@tvald
Copy link

tvald commented Aug 7, 2017

Eh, sure, I can see the potential for confusion. What about using ?: for nullary-coalesce, to parallel the conditional operator (for which it's sugar)?

Otherwise, we need a different two-character combination for optional chaining, since single special characters all present parsing difficulties. I suppose I could get used to something like ?!./?![]/?!(), which vaguely parallels TypeScript's non-null assertion (expression!).

@levithomason
Copy link

levithomason commented Aug 7, 2017

@claudepache thanks for the counter points, I'm in agreement with you. I was also not aware of the complimentary nullary-coalescing operator proposal. Per the stated goal of that project:

...the intent is to provide a complementary operator to the optional chaining operator.

I'd be in favor of using the cleanest syntax for optional chaining (perhaps ??) and updating nullary coalescing to play nicely.

Proposed nullary coalescing update

Looking at this operator in other languages for inspiration, here are some options:

  1. ?: - Somewhat nice as it plays on the ternary syntax, but might also get pretty confusing.
  2. |? - Also nice as it conjures ideas of the current || operator currently used in similar scenarios.

Of these, I'd vote for 2) |? strictly out of personal preference. It feels more like I'm saying "or this default" opposed to ?: which looks and feels like a mess to my eyes:

const val = a??.b |? 'a was null'

// vs 

const val = a??.b ?: 'what is happening'

Optional ??
Or default |?

@Mouvedia
Copy link

Mouvedia commented Aug 7, 2017

@tvald the elvis operator should probably mirror the usage of the ternary syntax and hence not be limited to undefined and null. But that's a bit off topic.

@levithomason your |? proposal is interesting.

@tvald
Copy link

tvald commented Aug 8, 2017

If consistent application of truthy/falsy vs nullish is important, and I agree that it is, there should be more of an effort to standardize this across the many proposals for shorthand operators.

This is the set of relevant operators that I'm aware of:

base truthy/falsy nullish
coalesce x ? x : y x || y (alt: x ?: y) x |? y (alt: x ?? y)
guard x ? y : x x && y x ?! y (link)
chain x ? x.y : x - x??.y

It feels like the set of possibilities for guard/coalesce are currently a mess, but that we want some recognizable parallel with the chaining operator. I think designing each of these operators independently will result in confusion, as they all inhabit corners of the same space.


edit 2017/08/08: simplified table by removing extraneous ternary row

@levithomason
Copy link

I'm not sure I follow some of the examples in the table. My proposal is ?? for optional chaining and |? for null coalescing (borrowed from F#). Is this mostly a heads up or is there an alternative proposal in there I've missed? Apologies for the lack of understanding on my part.

@tvald
Copy link

tvald commented Aug 8, 2017

Is this mostly a heads up or is there an alternative proposal in there I've missed?

Nullish guard shorthand (?!) was raised over in the proposal for nullish coalesce. (link)

Two comments above, @Mouvedia argued that elvis (?:) should be a truthy/falsy coalesce.

I'm just documenting the complete space of possible and proposed adjacent operators.

@tvald
Copy link

tvald commented Aug 8, 2017

This is how I would fill in the chart, for optimal symmetry:

base truthy/falsy nullish
coalesce x ? x : y x || y x ?| y
guard x ? y : x x && y x ?& y
chain x ? x.y : x - x??.y

(If this is too far astray from the original discussion, I can open a separate issue either here or in the nullary-coalese repo.)

@claudepache
Copy link
Collaborator

Using ?? for optional chaining and ?| for null coalescing seems a good solution for me. (For ?&, you should find use cases and share them in tc39/proposal-nullish-coalescing#4.)

A potential issue, is that ?? is quite frequently used for null coalescing in other mainstream languages. That doesn’t bother me, because if you mistakenly try to use ?? for null-coalescing, in most cases, you will get a syntax error.

@claudepache
Copy link
Collaborator

@Mouvedia

|? proposal is interesting. That's how F# does it.

No, your link just shows an example of custom operator definition.

@jridgewell
Copy link
Member

I'd be ok with ??., if only to get rid of the unsightly optional call in a chain:

// this is confusing
obj?.foo?.(args).prop

// a little better
obj??.foo??(args).prop

@littledan
Copy link
Member

@claudepache Even if it's not an issue formally, this still seems a bit confusing for developer intuition. I like how the current proposal includes ?, which creates parallelism with the ?? proposal and this feature in other languages.

@TheNavigateur
Copy link

TheNavigateur commented Dec 22, 2017

If ? must be in the operator then how about ?> ?

a?>.b
a?>[b]
a?>(b)

@littledan
Copy link
Member

@TheNavigateur Actually, I like that one!

@ljharb
Copy link
Member

ljharb commented Dec 22, 2017

Looks pretty similar to pipeline tho.

@rattrayalex
Copy link

There's another thread that talks about the syntax a bit more: #34

In an informal poll, ??. seemed to be popular: #34 (comment)

In any case, should this issue be folded into that one?

@TheNavigateur
Copy link

I think > conveys "propagate in this direction" (like an arrow) and I think the shape of ?> is distinctive enough from the |> (function call pipeline) and +> (function composition) operators as to be immediately distinguishable from them. All the other proposals overlap with other operators in at least some way. I just think this one probably offers the least friction and best clarity.

@Mouvedia
Copy link

Mouvedia commented Dec 24, 2017

Here's a proposal which resolves the cases that were considered problematic.
It requires to surround the expression with parentheses if you want to use the bracket notation, that's it.

// a == null ? undefined : a.b
a?.b;

// (a == null ? undefined : a[d]) + c
(a?[d]) + c

// throws (missing the : part of the ternary)
a ? [d] + c;

// just a backward compatible ternary
a ? [d]+c : d;

// throws (missing the ? part of the ternary)
(a?[d])+c : d;

// throws
e ? a?[d]+c : d;
// OK
e ? (a?[d])+c : d;
// OK
e ? a ? [d]+c : d : f;
e ? (a ? [d]+c : d) : f;

The real beauty in that proposal is that you will have an early error in previous ES implementation because the closing parenthesis is not found before the ? or at the end of the ternary.

@ljharb
Copy link
Member

ljharb commented Dec 24, 2017

That’s an unacceptable ergonomic cost - bracket access needs to be as seamless as dot access.

@littledan
Copy link
Member

I'm not sure if I agree with @ljharb on this stance on ergonomics in general, but I don't see how adding parentheses addresses the grammar issue. You'd still have to read ahead to the closing paren to figure out whether this is ternary or optional chaining, wouldn't you?

@pronebird
Copy link

pronebird commented Dec 24, 2017

Not sure if it’s too hard to do a lookahead. But if we would have to do a lookahead then there is no need to use parens until ternary expression involved. Then I find it reasonable to use parens to disambiguate the ternary part from the optional chaining.

@littledan
Copy link
Member

One of the concerns here, which ruled out ?[ normally, was allowing streaming parsers. Currently, in principle, JavaScript can be parsed by something which just works left to right, without multiple passes or backtracking. If you allow ?[ for optional chaining, you have to read ahead further in the expression to figure out what token you're talking about. It doesn't really help whether you force parens around it--parens are easy to match, but if you were willing to look ahead for a matching paren, you'd be able to look to the end of the statement too. The issue is looking ahead an arbitrary distance like that.

@hax
Copy link
Member

hax commented Dec 25, 2017

Though ?> idea seems ok for current JS, I worry about it being conflict with the usage of <> in generic type (TypeScript/Flow) or JSX.

@TheNavigateur
Copy link

Might only be as much conflict as =>

@rattrayalex
Copy link

I'm not sure (a?[b]) would require a lookahead (just an extra bit of state in eg; parseConditional or equivalent and a node rewrite, as we do for eg object patterns). Most JS parsers currently require a lookahead anyway.

But I certainly agree with @ljharb that requiring parens would be unacceptable.

Perhaps discussion about ?> should move to #34.

@Mouvedia
Copy link

that requiring parens would be unacceptable

Wouldn't be the first time.
cf #5 (comment)

@ljharb
Copy link
Member

ljharb commented Dec 25, 2017

@Mouvedia that should have been unacceptable, and could have been avoided. That's not a precedent to knowingly add another huge footgun along with new syntax.

@Mouvedia
Copy link

Mouvedia commented Mar 7, 2018

There's some kind of poll on #51.

@vcicmanec
Copy link

Why not think about it as a "?." operator with its own, slightly different, semantics, rather than spending a few years coming up a with a pristine proposal that won't satisfy all anyway?

@m93a
Copy link

m93a commented Oct 8, 2018

Why don't we just close this issue and stick to the current proposal?
It's easy to read (less visual clutter than ??), unambiguous and doesn't collide with the null coalescing operator nor with common sense – | is binary OR (or type union in TypeScript), : always separates name from value (or from type in TypeScript). Your new proposals don't seem lead anywhere nor do they solve any problem and .( is a thing I guarantee you'll get used to.

@claudepache claudepache added the alternative syntax past ideas and discussions about alternative syntaxes label Jun 9, 2019
sendilkumarn pushed a commit to sendilkumarn/proposal-optional-chaining that referenced this issue Jun 22, 2019
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