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 ?&

claudepache commented 6 years ago

Recall that this issue was opened specifically in order to resolve the inconsistency between a?.b and a?.[b] as raised in #5. From that perspective, &. has no benefit over ?..

codehag commented 6 years ago

right. sorry about that, i should have read the previous issue more carefully

hax commented 6 years ago

Another small problem of a&.b is, the symbol & looks too close to letters which lower the readability, though syntax highlight could solve this.

ljharb commented 6 years ago

@hax ! already has an existing meaning for parens and brackets. @codehag anything that only adds support for the dot, and not also brackets, is imo unacceptable. Symbols exist, and there must be a way to optionally look up Symbol-named properties.

hax commented 6 years ago

@ljharb As I understand a![b] or a!() do not have conflict with current usage of ! (note there should have no newline between a and !). TypeScript (and Babel 7) already support it! see https://github.com/tc39/proposal-optional-chaining/issues/34#issuecomment-350641176 for more details about my analysis.

js-choi commented 6 years ago

@claudepache: Recall that this issue was opened specifically in order to resolve the inconsistency between a?.b and a?.[b] as raised in #5. From that perspective, &. has no benefit over ?..

There’s also the alternative option of making x.[keyValue] valid as a regular property access, along with the optional access x?.key, and so forth. Was this alternative ever discussed? I just did a quick recheck and I haven’t seen it discussed here or es-discuss.

ljharb commented 6 years ago

I don’t think adding a second syntax for computed property lookup would work out very well; that seems like a high cost.

acusti commented 6 years ago

Bringing the conversation from PR #48 back over to this thread, my favorite syntax proposition so far is also !., ![, !(, as proposed by @hax. I understand there is a conflict with existing TypeScript syntax. Does anyone have an idea of how much trouble it would actually entail to migrate the non-null assertion operator to a different syntax, like !!?

Also, @ljharb mentioned that

! already has an existing meaning for parens and brackets.

As in !(foo && bar)? Seems distinct from baz.qux!(foo && bar). Is there another meaning you refer to?

Lastly, @Tiedye mentioned that ! usually implies inversion. While that is certainly true, the bang also often means required (as in TypeScript’s aforementioned non-null assertion operator or graphql schemas). With that meaning:

foo!.bar!()

Could read as: foo required (i.e. not null or undefined) for property access bar bar required for function invocation of bar

(An aside: @hax @lehni let’s make sure to continue the conversation in this issue thread, rather than in the PR thread, as requested)

ljharb commented 6 years ago

@acusti I haven't thought through the grammar conflicts enough - it might be workable - but I primarily mean an existing conceptual meaning. ! in JS means negation, and in Ruby/Rails it means "mutates" or "throws". Using ! for "optional" doesn't seem to mesh with the meaning of "!" anywhere.

jridgewell commented 6 years ago

Using ! for "optional" doesn't seem to mesh with the meaning of "!" anywhere.

This is my biggest negative for using a bang. It means assertion in typescript and ruby. Why would someone reading it think optional?

claudepache commented 6 years ago

If we use !./ ![, we will be unable to put a new line before the ! (which is a serious issue for my coding style), because the following is valid today:

veryLongFooAndBar
    ![baz]
TheNavigateur commented 6 years ago

Any further thoughts on ?> ?:

x?>.y
x?>(y)
x?>[y]

?

gisenberg commented 6 years ago

?> is close in syntax to the pipeline operator.

acusti commented 6 years ago

@TheNavigateur It looks like part of a scripting tag within an XML-based language, speaking as someone who wrote PHP in a previous career.

Based on @claudepache’s criteria, it:

Fulfills 1, 2, 3, 5, and 9 fully and without issue (is backward compatible; is reasonably easily parsable; works for static and dynamic property accesses, and function calls; uniform across the three syntactical cases; easy to type)

Partially fulfills 6, “partially suggestive”, thanks to the leading ?

Isn’t great for 4, “not used in mainstream languages for other purposes”, because it’s the syntax for the closing tag for PHP (as demonstrated by GitHub’s syntax highlighting given to it in @TheNavigateur’s comment)

Partially fulfills 7, “has precedent in other languages”, again thanks to leading ?, though that argument is somewhat compromised by the contrasting precedent in PHP

Could fulfill 8 if we got used to it and internalized the notion that the > suggests one part of the expression propagating in the direction of the arrow to the next part of the expression, though I personally don’t find it beautiful


Re: the pipeline operator, I find it visually fairly distinct, and only slightly more similar to the pipeline operator than to the fat arrow: |> ?> =>

It seems to me that the strongest argument by far is the fact that it is clearly and easily distinguishable from ?? (the current proposed nullish coalescing operator). As such, I think it warrants serious consideration.

And based on ES5 syntax, I would’ve argued that the strongest argument against it as that it doesn’t look “javascript-y”. In that light, I would actually argue that the pipeline operator in some ways functions as an argument for the proposal by making it look less alien to the language.

ljharb commented 6 years ago

@acusti it’s intentional and important imo that the nullish coalescing operator and optional chaining be similar, since they have similar semantics.

TheNavigateur commented 6 years ago

Isn't ?? close in syntax to the nullish coalescing operator? Doesn't this point hold more strongly since they exist in a similar problem space and yet have completely different meanings?

ljharb commented 6 years ago

They both deal with a possibly nullish LHS, which determines whether the RHS is executed. Seems pretty similar to me.

TheNavigateur commented 6 years ago

@ljharb They exist in a similar problem space, but the semantics of optional chaining and nullish coalescing as an expression are very different: propagation vs switching . If a developer misreads or mistypes a character the similarity is more likely to lead to bugs.

ScottRudiger commented 6 years ago

?. is the Best Choice

Throwing my opinion in the ring:

Coming from no language other than JavaScript, I strongly urge that you please stick with the planned ?.?.[?.( syntax laid out in the current README and implemented in the Babel v7 plugin.

Criteria

From: claudepache's comment

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:

  • ?. ?[ ?( — 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.
  • In general, Criterion 7 seems incompatible with the union of Criteria 1, 2, 3 and 5.
  • ?&. ?&[ ?&( — is what is proposed in this Issue. It meets objectively Criteria 1 to 5, and it fits well Criteria 6 in my opinion.
  • I’m leaving other proposals as exercise to the reader.

My Thoughts

To address the criteria as they pertain to the as-planned ?.?.[?.( syntax, here's my take:

Take, for example, this hideously golfed code that increments d from 1 to 2 (among other things):

(a={b:{c:1}},d=e=f=1,a?a.b?a.b.c?(d+=f,++f):(d-=e,--e):0:0) // current JS - increments 'd'
d; // 2

Under the currently planned ?.?.[?.( syntax, the following, shorter golfed code would still work as intended:

(a={b:{c:1}},d=e=f=1,a?.b?.c?(d+=f,++f):(d-=e,--e)) // '?.?.[?.(' syntax - still works
d; // 2

Under the proposed ?.?[?( syntax, however, the a?.b?.c?(d+=f,++f) portion of identical code would actually be misinterpreted as a method call and throw a TypeError because a.b.c is not a function, i.e., it would read it as (in current JS):

// '?.?[?(' syntax represented in current JS - breaks the code & fails to work as intended
(a={b:{c:1}},d=e=f=1,a?a.b?a.b.c?a.b.c(d+=f,++f):(d-=e,--e):0:0) // TypeError
d; // not evaluated because the above line throws 'TypeError: a.b.c is not a function'

edit: On second thought, the ?.?[?( syntax probably wouldn't throw a TypeError because it would be implemented with a lookahead that would eventually see the subsequent : portion of the ternary. However, it would certainly necessitate a much more complex lookahead (as @claudepache mentions in criterion 2) and make visually parsing the code much harder--especially if the : was located much further ahead in the code.

a side-note about the bang !, however: from the current README:

The following is not supported, although it has some use cases; see Issue #18 for discussion:

  • optional property assignment: a?.b = c

In the future, support might be added for ! to flip the logic; e.g., a!?.b = c to assign a.b to c only if a.b isn't already defined. It's a bit of a stretch, but perhaps the !?. would flip it from backward-looking (is a defined?) to forward-looking (is a.b defined?) and only set a.b to c if a.b is undefined, i.e., !a.b ? a.b = c : undefined.

There's likely a better behavior that ! could exhibit upon negating the ?. operator, but if we were to instead use !., it would begin to lose sense seeing, for example, a!!.b = c and leave less potential for some kind of ! support to be added later.

TL;DR:

Keep the proposed ?.?.[?.( syntax as is (for reasons).

Mouvedia commented 6 years ago

My take on this is still the same, keep ?. and use parentheses for the rest by using one of the 2 solutions Iv already presented:

It's logical and only becomes bothersome (requires to know what you are doing) if you do something less common.

hax commented 6 years ago

@ScottRudiger @Mouvedia I agree if uniform of three is not mandatory, the original ?. proposal is the best.

Two leading char solutions (like ??, ?&, ?>) are all suffer from ergonomics problem, more or less.

If we want both uniform and using just one leading char, the only possible solutions are:

  1. a!.b a![b] a!(b)
  2. a~.b a~[b] a~(b)
  3. a@.b a@[b] a@(b)
  4. a#.b a#[b] a#(b)
  5. a..b a.[b] a.(b)

So, make a choice 😈

hax commented 6 years ago

veryLongFooAndBar ![baz]

About linebreak problem, I believe mainstream coding style do not suggest

veryLongFooAndBar
  [baz]

but

veryLongFooAndBar[
  baz
]

So, you should write

veryLongFooAndBar![
  baz
]

in same way.

And, in theory we can allow

veryLongFooAndBar!
  [baz]

just as TS current usage of !, though it's a little bit weird.

ScottRudiger commented 6 years ago

@Mouvedia I agree; the option you presented in distinguish seems reasonable.

My take on this is still the same, keep ?. and use parentheses for the rest by using one of the 2 solutions Iv already presented:

It's logical and only becomes bothersome (requires to know what you are doing) if you do something less common (e.g. using brackets).

However, I think just sticking with ?., ?.[, and ?.( is easier from a visual/usage/ease of remembering perspective (but not necessarily from a language-implementation one?).

Imagine this, somewhat, non-typical use case:

const arg = 'fez';
const a = {
  b: {
    "some method": (arg) => console.log(`I like to wear a(n) ${arg}`)
  }
};

// w/ current JS - call "some method" with arg only if a && a.b && a.b["some method"] are defined
a != null ? a.b != null ? a.b["some method"] != null ? a.b["some method"](arg) : undefined : undefined : undefined;

// with ?. for dot & bracket access & method calls
a?.b?.["some method"]?.(arg);

// with optional (?) for dot access & required (?) for bracket access & method calls
a(?).b(?)["some method"](?)(arg);

Which option is preferable?

Personally, I think the version with ?. looks relatively simple & succinct.

However, the version with (?) begins to look slightly convoluted for my taste (there's something about too many closely packed parentheses that makes me perceive something as more complex than it necessarily is in reality).

Note: I (and most JS developers--I think) try to stick to the camelCase naming convention, but "method names with a space" have been known to exist in the wild.

TheNavigateur commented 6 years ago

Would there be a disadvantage to both allowing x?..y for consistency, as well as allowing x?.y as a permisiable shorthand for it?

The only disadvantage I can think of (at the moment) is confusion when reading the code, by someone who has been familiar only seeing one and doesn't know that the other is permissible.

claudepache commented 6 years ago

Adding a ”consistent” syntax in addition to an ”inconsistent” one doesn’t resolve anything, because the goal of consistency is to have less syntax to learn.

TheNavigateur commented 6 years ago

Yeah I broadly agree with this.

@hax Can you clarify what you mean by "ergonomics problem", e.g. with ?>. I am guessing you could mean "extra character to type". Yeah I think it's a little more effort to type, but once you've learned it, as claudepache suggested, the consistency means less to learn, both for writing and reading.

I'm curious if you mean something else though.

acusti commented 6 years ago

@ljharb I agree that having semantic familiarity with the nullish coalescing operator, while still making them visually distinct, is desirable. I was actually in the process of editing my comment right after posting it to clarify that my main issue with the ??.-based proposal is two-fold (but then real life interfered):

  1. foo??.bar foo??:bar are visually very similar
  2. ??: seems too long and arbitrary and entirely alien (not javascript-y)

I feel like ?? as the nullish coalescing operator is a very desirable outcome. It matches ||, but using a character that suggests a connection to optional chaining. ??: loses all of those advantages and feels like too much of a compromise in comparison. I find myself running into the nullish coalescing use case regularly (defaulting values that could come through as null), so I’m also very excited about that proposal and would really like it to have a reasonable syntax.

When I first read the proposal and saw foo?.bar?.[baz]?.(), I thought it seemed entirely reasonable and had no issues with it. I intuited that I could just look for ?. in an expression to identify optional chaining, and it felt consistent. I now understand how it is inconsistent, but I only got that upon reading about others’ issues with the syntax. Intuitively, the original proposal still feels consistent to me and always has. As such, I would second @ScottRudiger’s entreaty to keep the proposal as is.

If that is a non-starter, my second vote would be for ?>. ?>[ ?>(, which allows the nullish coalescing operator to remain ??.

hax commented 6 years ago

@TheNavigateur

About ergonomics problem, yes extra char is bad for input, but it also affect readability.

In mainstream coding style, we put spaces around operators. The exceptions are the highest precedence operators a.b a[b] a(b).

To correspond to them, aX.b aX[b] aX(b) should also has no spaces around in most cases. (I use X as the placeholder.) Without the spaces, if X have similar shape and darkness to alphanums, it will affect the readability.

Here is some examples:

Two chars: name3??.a_long_property??[computed_prop] name3?&.a_long_property?&[computed_prop] name3?>.a_long_property?>[computed_prop]

One char with high darkness: name1@.a_long_property@[computed_prop] name3#.a_long_property#[computed_prop]

One char with low darkness: name1!.a_long_property![computed_prop] name3~.a_long_property~[computed_prop]

For comparison: name3.a_long_property[computed_prop] name3?.a_long_property?.[computed_prop] (current syntax)

We can see both ?? and ?& are terrible, ?> is better, but still can not compare to one char alternatives.


Black -> White (from http://paulbourke.net/dataformats/asciiart/)

$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`'. 

White -> Black (use Consolas font, from https://dboikliev.wordpress.com/2013/04/20/image-to-ascii-conversion/ )

 `-.'_:,"=^;<+!*?/cL\zrs7TivJtC{3F)Il(xZfY5S2eajo14[nuyE]P6V9kXpKwGhqAUbOd8#HRDB0$mgMW&Q%N@

As these charts, ! and ~ are very low darkness char, which make them very recognizable

On the other hand, though @ have darkness higher than all alphanums, but its shape are more similar to alphanums than !~.

And, ?> is better than ?? ?&, because > is a low darkness char and have a special shape. But the avg darkness of ?>. is still higher than !. ~. and more close to normal alphanum.


Last but not least, all current JS operators are only use 1 or 2 chars (even !== just use 2 distinct chars), so I'm afraid ?>. ?&. use 3 distinct chars will look very strange and disordered to many JS programmers, at least in the first glance.

ljharb commented 6 years ago

@hax >>> and >>>= are both current JS non-word operators.

hax commented 6 years ago

@ljharb >>> and >>>= are also only use one or two distinct chars.

ljharb commented 6 years ago

As does ??:, ??., ??[, and ??( :-)

hax commented 6 years ago

As does ??:, ??., ??[, and ??( :-)

Yes, it does. My point of "3 distinct chars are unusual" is mainly reply to the "ergonomics problem of ?& and ?>".

Unfortunately, ??: ??. have a very bigger ergonomics problem I already raised in the PRs. And @acusti comment https://github.com/tc39/proposal-optional-chaining/issues/34#issuecomment-368905503 also give a good summary.

Compare to ??. ??: I would rather choose current syntax (?. ?.[] ?.() ??) , or if uniform of three is desired anyway, why not a?..b 😸

hax commented 6 years ago

I'd like to add another benefit of !. ![] !()proposal -- that is it avoid use of ? (also apply to other alternative like ~. ~[] ~() which do not use ? char)

See https://github.com/tc39/proposal-partial-application/issues/21

Consider combination of partial application and null-aware operators:

const g = o?.f?.(?, 3) ?? h -- current syntax, many ?s const g = o??.f??(?, 3) ??: h -- PR syntax, oh my eyes... const g = o!.f!(?, 3) ?? h -- ! proposal, clear! const g = o~.f~(?, 3) ?? h -- ~ alternative, a little more clear!

Of coz, partial application proposal could (and have to) choose another char if we insist use ? for optional chaining op. But only % ^ (maybe @) are available for them. Personally I prefer leave ? for partial application usage.

Tiedye commented 6 years ago

@hax Why would ? become unavailable to the partial application proposal if this proposal uses it? This proposal won't make (? or ,? valid character combinations.

Mouvedia commented 6 years ago

@ScottRudiger this line in your example

a?.b(?)["some method"](?)(arg);

should have been

a(?).b(?)["some method"](?)(arg);

Even if it's equivalent, for the sake of readability and consistency thorough, you ought to use the same token.

hax commented 6 years ago

@Tiedye Yes it's valid, but the readability is so poor (see example in my last comment).

And even it's valid, it still limit the syntax (then semantic), @rbuckton said "the optional chaining proposal is one reason why I did not allow ?.x() as a means of marking the this receiver for a placeholder" -- from https://github.com/tc39/proposal-partial-application/issues/21#issuecomment-361092565

Note, ??. syntax allow ?.x() in theory, but you will never want to read/write

g = ^? ??. f ??( ?, 3 )  // I intentionally add some spaces around the ops, but still eyes harmful
ScottRudiger commented 6 years ago

@Mouvedia

a?.b(?)["some method"](?)(arg); should have been

a(?).b(?)["some method"](?)(arg); Even if it's equivalent, for the sake of readability and consistency thorough, you ought to use the same token.

Agreed, that makes sense stylistically.

I guess I was thinking from the perspective that dot-notation is probably the most common use-case--putting myself in the shoes of a developer who uses ?. from time to time, but had to google optional chaining bracket-notation or somesuch search term to remember the syntax.

I'll edit to include a(?). though since it's the more sane style choice and since I happened to notice something else wrong with the comment too.

TheNavigateur commented 6 years ago

Wow, OK about ergonomics!

So then it's ergonomics vs learnability/intuitiveness.

?> is learned once as a consistent thing you insert when you want optional chaining.

?. is learned as a thing you insert when you want optional chaining, except before the . operator you don't need the extra ..

Personally, the 2nd thing bothers me more than the 1st, despite "ergonomic" superiority.

rbuckton commented 6 years ago
(a={b:{c:1}},d=e=f=1,a?a.b?a.b.c?(d+=f,++f):(d-=e,--e):0:0) // current JS - increments 'd'
d; // 2

This earlier example shows you can already write fairly hideous JavaScript code (though the same can be said for almost any language). JavaScript is full of tokens with overloaded meaning, for example:

In many of the above cases there are wildly different semantic meanings for the same token (i.e. blocks vs. object literals or arrays vs. computed property names). Overloading a token's meaning is nothing new, and isn't generally surprising. While I agree that an expression like o?.(?, b ? 1 : c ?? d) starts to look unwieldy, it is an expression that can be decomposed to be more readable.

If token length consistency for the chaining sigil is paramount, my personal preference for a chaining sigil would be the family of ?.. ?.[ ?.(, as it allows ?? to continue to be used for coalesce. That said, even if we did pursue either the ?.. or ??. family of tokens, I still believe that ? would remain a viable token for the partial application placeholder. I've even been considering leveraging prefix ?. ?[ ?( as part of partial application since it would be syntactically unambiguous.

The reason I feel that ? makes sense as an overloaded token for all of these scenarios is that they share a semantic meaning where ? means "this has to do with some information I don't know":

Even in TypeScript we use ? on some declarations with a similar semantic meaning:

All of this aligns with the general semantic meaning of ?, in that it indicates a question to be answered. Something missing that needs to be provided. To me that means some form of ?.. or ??. is the right fit for this proposal, as most of other tokens proposed don't align with that meaning.

hax commented 6 years ago

@TheNavigateur Not like ?? for nullish-coalescing, which is an exist syntax in broad used languages C#/Swift, all alternatives (except the original proposal ?.) of optional chaining are new to all. So I'm afraid learnability/intuitiveness is very uncertain. In fact, I believe whatever syntax TC39 finally choose, most JS programmers can learn it in one or two day. But ergonomics is hard to change.

PS. all the alternatives could have good mnemonics:

  1. a?&.b -- a && a.b
  2. a?>.b -- a -> a.b (if a is non-null forward to a.b)
  3. a!.b -- a != null ? a.b (!=null then chaining)
  4. a~.b -- ~ is just a perfect glyph of chaining

So I don't worry about learnability/intuitiveness at all :P

PPS. I found that I start to love a~.b a~[b] a~(b), Does anyone have the same feeling?

hax commented 6 years ago

@rbuckton I agree overloading meaning is usual and inevitable, but for one token, we'd better choose the meanings which rarely used together to ease the burden of our eyes.

Assume allow combination of null-aware ops and partial application:

o?.m?.(?, x?.[y] ?? z)  // current syntax
o?..m?.(?, x?.[y] ?? z)  // ?.. syntax
o??.m??(?, x??[y] ??: z)  // PR syntax
o!.m!(?, x![y] ?? z) // ! syntax
o~.m~(?, x~[y] ?? z) // ~ syntax

With partial application support ?. ?() ?[]

g = ???.f??(?, x??[y] ??: z) // PR syntax
g = ? ??.f??(?, x??[y] ??: z) // same as below, add space to avoid triple ? disaster
g = %??.f??(%, x??[y] ??: z) // PR syntax, with % for partial application
g = ?!.f!(?, x![y] ?? z) // ! syntax
g = ?~.f~(?, x~[y] ?? z) // ~ syntax

You can imagine other weird cases such as g = ???[?] 🤣

Obviously, use different tokens for optional chaining and partial application are much more clear.

ScottRudiger commented 6 years ago

@hax

I'd like to add another benefit of !. ![] !()proposal -- that is it avoid use of ? (also apply to other alternative like ~. ~[] ~() which do not use ? char)

See tc39/proposal-partial-application#21

Consider combination of partial application and null-aware operators:

const g = o?.f?.(?, 3) ?? h -- current syntax, many ?s const g = o??.f??(?, 3) ??: h -- PR syntax, oh my eyes... const g = o!.f!(?, 3) ?? h -- ! proposal, clear! const g = o~.f~(?, 3) ?? h -- ~ alternative, a little more clear!

Of coz, partial application proposal could (and have to) choose another char if we insist use ? for optional chaining op. But only % ^ (maybe @) are available for them. Personally I prefer leave ? for partial application usage.

If we absolutely must move away from ? for optional chaining, the ~ is starting to grow on me.

I think I could live with something like: const g = o~.f~(?, 3) ?? h -- ~ optional chaining, ?? null coalescing, ? partial application

But this just looks right to me: const g = o?.f?.(@, 3) ?? h -- ?. optional chaining, ?? null coalescing, @ partial application

or if @ really is not possible for partial application: const g = o?.f?.(%, 3) ?? h -- ?. optional chaining, ?? null coalescing, % partial application

I'd still prefer using ? over ~ for optional chaining because of the more clear meaning (in my mind) and closeness in association between optional chaining and null coalescing (not to mention the ergonomics of reaching for shift + ~ every time you need optional chaining. Personally, I think @ looks best for partial application, but could live with %.

staeke commented 6 years ago

@ScottRudiger Regarding use of ~. Although I agree that the programming language is mainly designed for English keyboard I think a reasonable design goal to be as usable as possible on others. This becomes more important for common operators as they're typed so often - something I'd argue is the case for optional chaining. ~ is a terrible character on several European keyboard shortcuts as it belongs to the family of characters that are to be followed by another key in order to type it on Windows. For instance, on Swedish standard keyboards you type "Alt Gr + Å, then lift all keys and then space".

ScottRudiger commented 6 years ago

@staeke ~. is not great on an English keyboard either, but that definitely sounds worse. By contrast, ?. is very easy to type.

hax commented 6 years ago

I don't familiar with European keyboards, but ~ (as Bitwise NOT) is already in JavaScript (and all C style languages) operators. So I will guess it's not a big problem for programmers at all.

Mouvedia commented 6 years ago

@hax How often do you use the bitwise NOT operator? Unless you are an adept of ~ preceding indexOf? Or the floor double tilde?

hax commented 6 years ago

@Mouvedia I rarely use bitwise operators in JS, and I against~a.indexOf() pattern. In fact as my daily eslint config, all bitwise operators are forbidden by default. But if I must write code for image processing, cryptology... I definitely need them.

My point is, though ~ as bitwise op only useful in specific domains, it exists in most programming languages for years. Also very common in unix command line (~ for user home path), and there are many other usages, eg. ~~through line~~ create through line in markdown... So it won't add much difficulty for programmers, they should already solve it or never mind it.

NOTE, I believe backtick ` also face the same problem (I guess in most keyboards backtick and tilde are coupled). But now it's heavily used for template string. So no reason to discriminate tilde from backtick.

ScottRudiger commented 6 years ago

This is going a bit deep into ergonomics, but despite backtick ` and tilde ~ being on the same key (standard US keyboard), I don't mind using backtick all the time for template literals or in markdown because you don't need to hold shift to use the backtick. That might not seem like much, but it's very comfortable to just hit the ` key, but quite uncomfortable (for me) to keep my little finger on the left shift key while pressing ` for the tilde ~. You could tell me to use the right shift button, but I don't think I'd ever get used to that.

hax commented 6 years ago

@ScottRudiger To be fair, ? also need press shift, I use left shift but I also know many use right 😉

Of coz ? has a much better position compare to ~ in normal keyboard, but | is also in the right-most place like ~ in the left. On my Macbook Air 13' keyboard, F to ~ is about 98mm, J to | is about 111mm 😅

ScottRudiger commented 6 years ago

@hax it's not about just having to use the shift key, it's about the way my left hand has to contort itself (lol) to reach left shift + ~. But you made me realize, I don't always use left shift. Literally the only time I use right shift is for ? and they're right next to each other.