-
Notifications
You must be signed in to change notification settings - Fork 75
Defining precisely the scope of short-circuiting #10
Comments
Seems like these are in order from what I'd prefer the most to the least. I would expect at most 2, which provides a simple syntactic rule. |
While I struggle to think of cases where (3) would be useful, I am curious if there is something that seems semantically harmful about supporting it? In case it aids the discussion, here are examples of bad code I could imagine someone trying to write: const fancyAlerting = (analyticsApi, loggedInUser = null) => {
const fancyUserProfile = new loggedInUser?.Profile();
analyticsApi.track(fancyUserProfile, 'someAction');
// where `.track()` gracefully handles nullish first args
};
const fancyErrorPopup = (fancyError = null) => {
const errMsg = fancyError?.format`
You pushed the button wrong!
`;
maybeAlert(errMsg);
// where `maybeAlert` gracefully handles nullish first args
}; There are obviously other, probably better ways of expressing these concepts; I don't mean to put this forward as something that I personally want to do. |
I think it's better if we don't add obscure and unintuitive instances of syntactic sugar. |
While that would certainly be obscure, I'm not sure it's unintuitive. For example, I would find it quite unintuitive that It's been my rough impression that in the past TC39 has erred on the side of consistency, even in obscure cases like these. Has the committee (or perhaps, some members of it such as yourself) come to regret those decisions? Or perhaps I have the wrong impression. Separately, and I hope this does not come across the wrong way – given your comments in other threads, I am curious if you have used |
Consistency is one thing, and then defining obscure edge cases to have a particular obscure behavior is sometimes another one. I can definitely say I disagree with some particular past decisions on certain edge cases (as I've proposed changes to them). In my opinion, TC39's job should be to work out the details for the simplest consistent feature design that solves programmers' problems. No, I haven't used |
Thanks, that's helpful context. I imagine your team may also be among the
most affected by decisions like these, since they impose additional testing
(and possibly implementation*) burden.
* IIRC, it actually would have been more work not to support these edge
cases when I implemented this feature in my fork of Babylon, but that might
be quite another story in the Chromium codebase.
My hypothesis is that the utility of option 2 over option 1 would likely
become evident quickly through use; if you have a Babel-processed codebase
lying around it might be worth trying `?.` in a few places where `== null`
or `obj && obj.mem` are currently used (though I don't mean to tell you how
to do aforementioned homework).
…On Sat, Jul 15, 2017, 12:40 Daniel Ehrenberg ***@***.***> wrote:
Consistency is one thing, and then defining obscure edge cases to have a
particular obscure behavior is sometimes another one. I can definitely say
I disagree with some particular past decisions on certain edge cases (as
I've proposed changes to them). In my opinion, TC39's job should be to work
out the details for the simplest consistent feature design that solves
programmers' problems.
No, I haven't used ?. in other languages; maybe that leads to naivite
here on my part. Sorry for that; I'll go do my homework.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#10 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAq_LoClkoooXhd1dU-Z1eAimw6aXN00ks5sORWVgaJpZM4OY5ab>
.
|
I would be surprised if either option worked - iow, having ?. extend to invocations does not seem intuitive to me, whether () or tagged template. |
@ljharb yes, you've made that clear elsewhere 😄 I'm curious what environments you've used I've searched far and wide for signs of confusion in the coffeescript community about that behavior (stackoverflow, github issues, etc). CoffeeScript users show plenty of signs of confusion/surprise, but I've come up dry on this topic. Have you used a language with this feature and found the behavior surprising? (Sidenote: if anyone is looking to quickly+easily experiment with |
I have not used this feature in other languages, including CoffeeScript, but that perspective is still potentially more valuable than that of somebody who has used it. Lots of things are idiomatic in other languages, that are not idiomatic in JavaScript - it's important not to forget that. |
I certainly agree that the perspective of a newcomer to the feature is very valuable. It so happens that my personal hunch would be that if you showed a novice JS person the scrap
I'm curious what you have in mind? The only relevant JS idiom I can think of would be If this is the wrong place for these conversations, feel free to react with a 😄 rather than a comment – I don't mean to badger if this isn't helpful. |
Nothing specific. However, |
OK, this isn't evidence of me using the feature, but from the C# standard, it looks like C# implements option 2/3 from the above, rather than 4. I don't know enough about C# to know whether it has any more call/property-access-like operations; I don't see any. I apologize for bringing up short-circuiting in this thread. Let's stick to #3 for that. For me, I feel more comfortable with the short-circuiting concept if it can be explained syntactically, as C# does, rather than by the propagation of a Nil value. Although both of these are spec devices, it seems like they leak out into the mental model that programmers will use when understanding the scope. |
I have just updated my spec text, I have no strong opinion on whether short-circuiting should allow to bypass |
FWIW, I've just reverted my spec text to Option 4, as it was just as simple to spec, contrarily to what I thought (I feared that I had to add complication to static semantics rules, but finally: no). |
My preference would be for something in between options 1 and 2 (I'll call it option 1.5): special-case the syntax I think special-casing the Option 1.5 means you need to use Another thing to keep in mind is that any short-circuiting makes tools harder to write. Operations like "extract method" and "extract variable" suddenly need to be very careful that they're not changing the behavior. With tools like jscodeshift and AST Explorer, it's becoming more common for regular developers (not just "tools people") to write code transformations, and I worry that adding more complexity to the evaluation rules would make it harder for people to safely write things like automated refactors. |
It's a fair point that |
@ljharb Right, to be clear, my proposed option 1.5 behavior is that optional chaining only works on the forms Examples:
|
@alangpierce it sounds like short-circuiting extends beyond that roughly 7% of the time. Is there an easy way to see a few examples of that and see how reasonable they are? |
Sure, I hacked my tool to print out all examples and ran it on the Atom codebase. Full gist here: https://gist.github.com/alangpierce/08611d4d1cf5572fc5a272d7468b96d6 Here are all 364 soak operations in the Atom codebase: Here are the 159 (44%) soak operations that use short circuiting: Here are the 17 (5%) soak operations that use short circuiting that isn't just a method invocation (i.e. cases that wouldn't be handled by my option 1.5): (Each file is a subset of the previous file.) |
Thanks very much for sharing, @alangpierce ! Seems like an instructive set of examples, especially those towards the end of the last gist. Seems there are many cases of "if a is not null, then we expect it to always be of some shape, and use that shape directly" – whether that is a string that will match a certain regex, or a nested object with methods. Some examples that seem representative to me, in no particular order: while selection.end.row is selections[0]?.start.row @nextSibling?.model.setFlexScale(1) args?[3].match /Desktop/i line.match(indentRegex)?[0].length |
@rattrayalex Yep, agreed that "optional object with a known structure" is a real use case, and that it's solved well by short-circuiting. I think it's a matter of tradeoffs; I would argue that any kind of short circuiting behavior adds complexity to the language, and that the right way to frame the discussion is to weigh the cost of that complexity with the benefit it provides. My gut feeling on the cost is that it's unsettling to change the language in this way, but I also wouldn't be super-opposed to it. My gut feeling on the benefit is that it's sometimes useful, but doesn't really come up that much, and usually the alternative isn't so much worse. It certainly doesn't feel like short-circuiting is critical for With your specific examples, I think it's also worth noting that the first three of your examples can be written using
For that case, my best idea in the "option 1.5 world" is to just do |
IMO, it is is arguably more brittle and less clear:
Those are the two reasons I want short-circuit (at least Option 2 in order to encompass the most plausible cases), as I've already expressed in #3 (comment): debuggability and expressivity. |
The ongoing refactoring of Issue #20 :
|
I’m closing this. If you have still problems about short-circuiting, first read the FAQ, item “Why do you want long short-circuiting?”, then comment in Issue #3 or other appropriate place. |
From #3 (comment):
The rule is more precisely:
If the part at the left of
?.
evaluates to null/undefined, the rest of the current subexpression is not evaluated, and the whole subexpression evaluates immediately toundefined
,where the meaning of “the current subexpression” should be clearly defined, which is the goal of this Issue. “The current subexpression” may be:
a?.b
— which is less useful if it is immediately followed by another property access, method or function call as discussed elsewhere;a?.b.c().d[x]
;new
and tagged templates (which are sort of method/function invocations), e.g.new a?b.c().d[x] `{y}`
;(new a?b.c).d[x] `{y}`
.I chose option 4 ; although, as a user, I’ll be quite indifferent to anything between 2 and 4.
Some additional remarks:
If the inclusion of
new
and/or tagged template is judged confusing, one could statically forbid (i.e., “syntax error”) construction of “left-hand-side expressions” that containsnew
or tagged template somewhere after a?.
. I expect that the need of such a construction should be rare in practice anyway.About grouping parentheses as in
(a?.b).c
. One must take a decision, but any option will have very limited practical impact, because:(a.b).c
;a?.b.c
— except in some constructs incorporating thenew
operator, such as(new a?.b).c
... that should be rare (and could be statically forbidden, see previous bullet).The text was updated successfully, but these errors were encountered: