-
Notifications
You must be signed in to change notification settings - Fork 75
Proposal: Drop optional call expressions #59
Comments
If we do (I'm neutral on keeping vs dropping optional call, but I don't think dropping optional call solves any of the issues you're mentioning) |
To be clear, I don't think it's a hard requirement that any of the operators share the base. I just can't stand |
I understand; but i, at least, do consider it a hard requirement. |
Optional call has significant use cases (~12% (= 576/4627) of all soak operations in the sample of CoffeeScript code from #17), and should not be dropped because the syntax looks ugly. Also, if we sidestep the issue by not implementing that case now, and it becomes evident later that we really want it, we will have a more constraint choice in the future due to what is already in place. We’d better solve the syntax issue now. |
What are the actual uses in coffee script code? Right now, I think Why not think of this as an separate operator? It already has drastically different semantics than optional property access. |
One more modern usage pattern might be: const MyComponent = ({onClick}: {onClick?: Function}) => (
<div onClick={() => {
someSideEffect();
onClick && onClick(); // onClick?.();
}}/>
) (Personally I agree it is less appealing, semantically different, generally weird/unnecessary, and best found in another proposal, but thought I should share that example). |
@jridgewell even if this was a separate operator, the consistency argument still applies to dot and bracket access, so dropping or separating optional call has no impact on the operator choice of optional member lookup imo. |
Note that, although we speak somewhat abusively of optional property access and optional call, it is in fact an entire chain (of property accesses and function/method calls) that is optional. This is the case in order to have short-circuiting semantics that is both most useful and syntactically determined (#20). From that perspective, it has by no way ”drastically different semantics”: the semantics of an ”optional chain” is the same whether it contains a function or method call at its beginning, or in its middle, or nowhere, and the semantics of a function call is the same whether it appears at the beginning of an optional chain, in the middle of it, or outside of it. The reason that the three cases ( Now to the point: Splitting the proposal in two won’t help to resolve the syntax issue... at worst, it could worsen it ( |
Out of curiousity, would you still hate optional call expressions if #48 changes it to |
Like I said in #61, I personally don't see any value in optional calling if the operator won't check if the property is callable. This could end up with something like This is discussed in #2 as well, and I think is a valid concern to drop optional function calling from this proposal, because this only check if an object is |
I consider that having a non-nullish non-function where an optional function is expected is most probably a bug, and it is not worth to deviate from the general ”non-nullish” semantics (common to optional-chaining and null-coalescing) in order to support that specific dubious case. In general, by design, for the sake of not making bugs more difficult to detect, optional chaining is not about a broad ”suppressing exception” but a precise ”checking for nullish”. But if you disagree, i.e. if you think that |
What I said, is either the proposal checks that is a function, that I personally don't think it should, or the proposal don't support for a callable operator, meaning that you can't optionally call a function, because you have no actual way to know if what you are trying to call is a function. The latter I think would be better. So I think we both are saying the same thing at the end. 👍 |
Why not the following? Error.captureStackTrace(this, CustomError)? |
What is the semantics of that line? |
If class This example comes from the Custom Error Type https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types Personally I'd like to use such functionality with function* buildSteps(metaBuildList) {
yield metaBuildList.startMessage()?
yield metaBuildList.printList()?
/*
Some builders implement to make package.json from package.json5, and also
may have a personal project structure that needs to put some config files in
the root while tools need them there
*/
yield metaBuildList.provisionRequiredFiles()?
// Some builders are used in environments without symlinks
yield metaBuildList.mimicSymlinks()?
// Some builders may not implement as they're static
yield metaBuildList.updateDependencies()?
yield metaBuildList.lint()?
/*
Some builders may need to run through their checklist before all build
features are implemented
*/
yield metaBuildList.deploy()?
} Of course my own ulterior motive is to organize my design by contracts code in manageable, succinct lists. I think the above is easy to understand and brings functionality that would traditionally be verbose. Hopefully this makes sense. |
Also I didn't say this, but I mean to include optional chaining as well: Edit: I figured that part out. I propose to use the above as I expected. Then do Edit 2: After running through how to not leave out any functionality, I'll have to let go of a simple question mark at the end as the operator for "check for property and call":
Same pattern for arrays. Chains should simply go at the end of the question marks. I honestly can't think of another variation without it getting complicated |
Ok; this is exactly what ”optional call” is intended for: Error.captureStackTrace?.(this, CustomError) Note that the ”optional-chaining operator” |
I see now. The period in between the property and calling the property gives a notion they're detached. Is it audacious to ask developers get syntactic sugar to imply that Error.captureStackTrace?.(this, CustomError) and Error.captureStackTrace?(this, CustomError) are the same? Edit: Also, I'm curious if the period is ever needed if a question mark is used. Could it be implied in all cases? |
No, because of ternary operators. |
Do ternary operators include at least 1 space on either side of the question mark? I'm thinking since optional chaining operators require to hug their subject, could it be possible optional chaining without a period is still incompatible? |
No spaces are required around the ? or the : in ternary operators. ? isn’t an option. |
Ah interesting. Thank you for answering my questions :) |
@andrewmiller1 the complexities of supporting |
That provides a lot of insight into the issue. Thank you @lehni |
I completely agree with the idea of dropping optional call. |
I agree than the dot in
|
@caub why obj.fn?(arg1) |
@jridgewell I can't seem to find your rant against |
It's still there, but there are so many responses that it may not load: I feel like I need to register my distaste for // Ick.
obj.func?.(arg);
// Why the hell are there parenthesis in my member expressions?
obj.func?.(arg).property;
// @#$*
obj.func?.(arg.property); The first example is acceptable, but the second (a member expression chained onto an optional call) is just the most confusing syntax. I literally parse it as ....... |
technically won't you rather need to do: obj.func?.(arg)?.property since you don't know if const obj = {}, arg;
obj.func?.(arg).property |
Not necessarily, the return value of the function may be known to be an object. |
@jridgewell hypothetically speaking, to find out if you're against the operator altogether, or its current syntactic form, how would you feel about this, assuming a language where obj.func?(arg);
obj.func?(arg).property;
obj.func?(arg.property); I feel that here the potential confusion you talk about is not an issue, as This then raises the questions:
|
Personally, I am more sensitive to the elegance of the algorithm than of the surface syntax. Without optional call, you may need to use temporary variables, to repeat yourself, to use boilerplate, ... Naturally, it is different if the syntax is confusing (not just bad-looking); although I doubt that it is much more confusing than |
What's the argument for dropping call expressions? I hear mainly that there's supposedly no use-case or that it's "ugly". So what's the fear? Is it that it's gonna sit in spec, unused, or that people will find it useful (e.g. in our codebase we have dozens of It can't be both. If it's the latter, then this issue should really be about "let's make the syntax prettier". But, as @claudepache, if the decision is between "ugly" syntax, and no syntax (i.e. dropping it), then I lean towards "ugly". WTBS, current short-circuiting implem will make it less useful than e.g. the above mentioned |
I agree. If we didn’t have the
I disagree, I think it’s much more confusing than the optional bracket symbol. |
I think that we only have 2 viable path left:
TC39 recently voiced that |
There are async arrow functions, where the parser has to differentiate between |
I believe there was some discussion about this issue at a breakout session at the November 2018 TC39 meeting. Does anyone have notes or a summary from that discussion? |
We had a very productive breakout session where we discussed all parts of this proposal. After looking at it from many angles, we reached a consensus that the proposal as is is the best path forward. With one pending opinion, I’ve been assured support for Stage 2 in March. |
Just tossing this out there. If the That's nice and explicit and doesn't muddy the waters. And there's precedent, as this exactly follows what C# does to handle potentially null delegates: And if using |
That would mean that |
No, because |
he surely meant I think it's a good idea, because most of the added value is in optional property chaining anyway, not really optional calls, because a consumer of an API is supposed to know whether a property is a function or not, if it exists and also because I believe in https://github.com/Agoric/proposal-infix-bang#infix-bang coupled with this proposal, and by reducing the syntax overhead here, it can help tc39/proposal-wavy-dot#8 |
Again, |
I don't think this is going to change, as this is now stage 3. But, the
seems fine to me. |
Right, ok, I still don't see many concrete usecases for <div>{children?.(data) || React.cloneElement(children, data)}</div>
// vs
<div>{typeof children === 'children' ? children(data) : React.cloneElement(children, data)}</div> that's just my reserve, but this proposal for the true optional chaining is excellent indeed |
@michaeljota from the TC39 process
seems like there's still time |
That's exactly what I meant, yes. And indeed, what I forgot was that:
Hence the idea for a different proposal that allows invoking a function using the original context, e.g. Also, with a proposal like the infix bang to chain promises you'd already end up with something quite hairy if you want to execute a function returned from a chained promise, if you don't go down the road of an explicit invoker method: let result = await maybeCreatesFnAsync()!?.(...args); I'd take let result = await maybeCreatesFnAsync()!?.invoke(...args); over that, for instance. Even if it's longer, it's far more readable. |
Currently, There exists a stalled proposal that includes saving the original context of a method, see: https://github.com/tc39/proposal-bind-operator; but (using the current syntax of that proposal) |
Now that we are at Stage 3, we can consider that the semantics are fixed (except for the relatively minor issue of private field access, see #28). In particular, optional call is not dropped. |
Now that I'm getting used to it, I'm actually finding more places where optional call is nice to have. const opts = Component.opts && Component.opts() || {}; We have this currently for a const opts = Component.opts?.() ?? {} Not the prettiest, but it's nice enough. |
@jridgewell You snipped should work just like the first one. This, now outdated, discussion, was about whether or not this operator should check if a property is a function before trying to make a call, but you are not checking if |
One of the biggest blockers for the "optimal" syntax
?.
is the optional chain call?.(
. I hate it, and a few other delegates agree.I don't, however, share the requirement that all three operators share the same base (the whole
??.
operators). I think?.[
is ugly, but it still means property access to me. Even in deep chaining sequences, it still makes sense to parse (compare this with my rant against?.(
:It's obviously not great, but it's not a total deal breaker for me.
So this leads to my proposal. Why not just drop the optional call entirely (to be clear, not the short-circuiting of method calls, just the
?.(
optional call)? I don't have any major use cases for it.The text was updated successfully, but these errors were encountered: