-
Notifications
You must be signed in to change notification settings - Fork 75
"Short-circuiting" feature #3
Comments
This is related to #2. The question is when does the |
@xtuc Yes, it's related; I had missed that bug. I meant this bug to track a more basic question: do we need Nil at all, or can we just say that |
I’ve planned to give an alternative informal description of my envisioned semantics in terms of abrupt completions instead of Nil reference; I’ve not done it yet due to some IRL concerns, but I promise to do it the next few days. I hope that that alternative description will convince that there is more locality than what is apparent at first glance.
In that context, the “right-hand side” means basically “everything that is on the right of |
@claudepache Sorry, I guess I meant that "where does the right hand side end exactly" question a bit more rhetorically--you definitely defined things clearly, but from a user perspective, is |
Can you look at the comments in #2, there are example of that. |
The short answer: the precise place where the short-circuit ends is where it is the most useful for the user, while remaining relatively simple to understand. It is a balance between usefulness and simplicity. For the long answer... I expect to write it soon in my repo (also, after I’ll have read #2). |
Note that you can get the "other" desired semantics by doing // proposed
x = a?.b.c().d
y = (a?.b).c().d
// vs
x = a?.b?.c?.()?.d
y = a?.b.c().d where What if a.b is null. if I want // proposed
x = a?.b.c().d
// vs
x = a == null ? null : a.b.c().d which, clearly, is not using the operator at all, completely defeating it's purpose. This is why the current proposal is more "local", as it incentivize users to put |
While @xixixao 's case should be sufficient to show that "short-circuiting" is the only usable form of this feature, here is another argument in its favor: given type User = {
id: string,
// ...
address: {
street: string,
// ...
}
}
function getUserIfExists(id): ?User {
if (users[id]) return users[id];
return null;
};
const user = getUserIfExists('user_abc123');
// as proposed:
const street = user?.address.street;
// without "short-circuiting":
const street = user?.address?.street; The latter version doesn't make much sense; a However, without short-circuiting, we would be forced to write Thus, the genuinely incorrect case of |
Short-circuiting on member access is fine, and obviously the proposal is almost useless without that. I'm suggesting that extending that short-circuiting to function invocation is problematic. |
Ah, perhaps I misunderstood what exactly is meant by "short-circuiting" (judging by discussions in the babel slack channel #proposal-opt-chaining). eg; one committee member seems to desire:
...which seems like very surprising behavior to me. EDITED EDIT: I believe @ljharb was responding as if we were on another thread (#2) as it's a bit unclear what the distinction between them are. It's my impression that @ljharb is in accord with @xixixao , @jridgewell, myself, and others on this particular thread, but in disagreement on the question of #2. Apologies for the confusion 😄 |
@rattrayalex: That particular issue is being discussed in #2. This one is concerned with: a?.b.c; // a = {b: { c: 42 } }
a?.b.c; // a = null From OP:
Specifically, the second case should throw (you have to use |
@jridgewell to clarify, in both cases, |
@jridgewell @ljharb does everyone on TC39 agree with:
If so can this issue be closed? Pardon my confusion 😄 |
Lol, I guess there are 3 cases: a?.b.c; // a = {b: {} }
a?.b.c; // a = {}
a?.b.c; // a = null OP would have us throw in case 2 and 3. I argue that only case 2 should throw (because |
@rattrayalex no, I don't think the committee has consensus on that either. @jridgewell thanks for clarifying. |
OK, from the above thread, it sounds like short circuiting is important specifically in the case where you do want to throw an exception if the implicit JSON schema is not being followed. Is that the idea? |
Here is the main reason I want short-circuit. When I write: a?.b.c().d I assert that I gave the same argument one year ago on es-discuss with a more illustrative (although synthetic) example: https://esdiscuss.org/topic/optional-chaining-aka-existential-operator-null-propagation#content-12 A second reason is expressibility: I want to write |
@littledan that's one major reason. Verbosity is another - notable because the primary intent of the feature, as I understand it, is to reduce the verbosity of |
If |
@marlun78 what you want is |
@claudepache Thanks for explaining; that makes sense to me. @marlun78 If you don't want it to throw, you could use |
So is |
@marlun78 I haven't seen a proposal for this use of |
Okay, so I misunderstood @Mouvedia’s comment... |
Some comments about the original objection (#3 (comment))
|
I'm not sure how mature the feature is. But what I want to see is a operator like typeof that could "see" down the path. Like
This kind of solves the problem of if(obj && obj.foo && obj.foo.bar && obj.foo.bar.baz) ... It wouldn't introduce new syntax. The operator would return the value of the property itself or undefined if it can't reach the last property. There's also de benefit that you can use it in conjunction with typeof and instance of. |
An operator is new syntax; that's what this proposal is. You're describing new syntax that has automatic short circuiting all the way down the chain. |
Also, note that the current proposal is more precise. That is, you have to write: obj?.foo?.bar?.baz in order to get: obj != null && obj.foo != null && obj.foo.bar != null ? obj.foo.bar.baz : undefined with a one-to-one correspondance between |
The typeof already have if(IsUnresolvableReference( V ))
What I'm proposing is something equivalent to this, but in a operator. What I meant about syntax, it is indeed a new reserved word, but it doesn't introduce new symbols, it would be just another UnaryOperator. |
Yes, and this is what I don’t want: I want the more precise |
Instead of: obj?.foo?.bar?.baz Would it be possible to support?: (obj.foo.bar)?.baz |
@garygreen that should just work, i'd expect - with or without the parens. |
You could have just done |
I'm not sure but I think @garygreen was suggesting that with parenthesis it would be equivalent. If I understand correctly they aren't equivalent and this will error when obj.foo.bar?.baz
//or
(obj.foo.bar)?.baz Where as this won't: obj?.foo?.bar?.baz I'm not for or against this suggestion but I just read it differently so I think it needs clarifying to avoid confusion. |
Yes exactly, if your wrapping in parenthesis I personally would expect it to mean everything in the parenthesis is optional. So: (obj.foo.bar)?.baz Should (IMO) be the equivalent of: obj?.foo?.bar?.baz I understand specifying the optional chaining It's possible this has already been considered though, so I'm just throwing the idea out to see if it's feasible. |
Please just give me the time to find out where I’ve written that, so that I repost it here... Ah, it was in #82 (comment): By design, you have indeed to write That was already discussed in #29; see in particular #29 (comment) and #29 (comment), and the FAQ entry on the README that begins with ”In a deeply nested chain ...” |
@garygreen the paren version, if it parses, needs to be the same as: const o = obj.foo.bar;
o?.baz or else I’d consider that a design flaw. |
@garygreen It is not a design flaw, it is a design feature. What you are asking, is that the meaning of Like other operators, You are asking that |
Closing this issue, since we attained stage 3, and short-circuiting is an important part of the semantics. |
In this proposal, as the explainer says,
The "short-circuiting" semantics received some criticism at the previous TC39 meeting where the proposal was discussed. A few committee members (including Waldemar Horwat) expressed a preference that this would throw (on the access to .c) to keep the semantics simple and more locally understandable, and refrain from changing
.
. The explainer talks about the "right hand side", but where does the right hand side end exactly?If you don't plan to remove this feature, it would be useful to extend the explainer with some more motivating use cases.
The text was updated successfully, but these errors were encountered: