-
Notifications
You must be signed in to change notification settings - Fork 25
RFC 0021: optional chaining #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@garu I agree with everything you have written in this pull request, and have no improvements to suggest. In particular I agree with this being explicit rather than implicit, naming the operator |
Note, you have a typo in your example code, a |
I hope it's not too late to ask this: why not add |
Thanks, I‘ve overlooked the method call example. |
@abraxxa: If two characters are preferred, I'd strongly lean towards @garu: But I'm not so sure that I agree that $val = $data?->{deeply}{nested}[0]{data}{value} instead of: $val = $data?->{deeply}?->{nested}?->[0]?->{data}?->{value}; |
Thats interesting. or maybe ??-> for the rest of dereferencing ie. and ?-> for "this chain only" ie. |
I think its better to keep it simple that Likewise, I disagree with the idea of |
Come to think of it, if this doesn't already happen, an additional complementary operator, say |
I‘d prefer that over a bulkload of additional operators. |
Your suggestion doesn't help when using both versions of the operation in the same expression or statement, eg like how would |
What are you expecting to happen from this construct? |
I expect that some of the lookups will raise an error if the element doesn't exist, and some of the other lookups will instead substitute the empty list. |
And this error on missing index should be triggered by what? Currently Perl would autovivify it, my proposed |
So we would seem to be in agreement then that these should be two complimentary features rather than an either-or choice. My issue before with your suggestion was that having |
Yes, I think the feature is a better alternative to the |
An autovivification feature that could be disabled would be nicer for dealing with deep data structures, but it doesn't help with method chains. |
Hi everyone! Thank you for all the input provided so far, I really appreciate it and will accommodate what I can on an update to this PR later today. Meanwhile, let me try and address some of your comments below. This RFC is NOT just about autovivification, it's about short-circuiting the dereference arrow on undefined values. You can already disable autovivification via the
Even if this feels like a consistency bug in perl itself, that behaviour has been around for so long that backwards compatibility dictates it probably won't be fixed, so the optional chaining operator provides yet another advantage (but, again, not just that). Providing the definedness check on arrow operators via a pragma is unfortunately not good enough as it (a) doesn't give you enough granularity in, say, Regarding the need to be explicit in chained calls instead of having it "toggle" the feature, I understand and agree that it would be nicer to write (in fact, the original draft of this RFC had this feature), but after looking at a few examples and discussing it on p5p, it was clear that it could be harder to read, and make it easier to miss some errors (since it would be so easy to just leave it on after the first Adding a modifier to make the optional chain "greedy" (like Finally, I'd like to invite everyone reading this to join in on this and other design discussions on the p5p mailing list, reserving this space for issues with the PR itself (typos, need for clarification on a certain section/example, etc). That way everyone is able to contribute and we keep everything centralized <3 |
* typo fix on example code; * fix paragraph that approaches CPAN emulation of the feature; * move mention of optional chains in string interpolation / regexes from "Open Issues" to "Future Scope", effectively addressing all open issues (and added nod to template literals);. * expand "rejected ideas" FAQ to include rationale over lexical pragmas and related ideas, while also deepening the explanation of why it should be explicit.
A thought occurs to me - after a $foo?->{a}{b}{c} With the behaviour as described (where subsequent chainings are mandatory) in the above example - if So surely it makes sense that after a |
You are right, the optional |
Maybe I misunderstood, it feels like you're considering just the case where the optional chain returned undef. What if it doesn't? my $foo = {};
my $c = $foo?->{a}{b}{c}; After the code above is executed, I expect $foo to be my $foo = {};
my $c = $foo?->{a}?->{b}?->{c}; Then I expect $foo to still be just As for mixing mandatory and optional chains, let me give a real-world example: a certain famous global payment gateway's REST API provides a webhook that sends a POST request to your web application with JSON. That object, if present, may or may not have a "transactions" key. If it does, it is (by "contract") supposed to be an array with at least one element which always contains a "related_resources" key, which itself should be an array with at least one element and that element may or may not have a "refund" key containing refund data. So to see if you have the refunded successfully, you have to: my $refund_state;
if (defined $json
&& defined $json->{transactions}
&& defined $json->{transactions}[-1]{related_resources}[-1]{refund}
) {
$refund_state = $json->{transactions}[-1]{related_resources}[-1]{refund}{state};
} With the new optional chaining operator, it becomes: my $refund_state = $json?->{transactions}?->[-1]{related_resources}[-1]{refund}?->{state}; So while "transactions" may or may not be there, if it is, it must be an arrayref of at least 1 element which itself must contain a "related_resources" key, and so on, until you reach the (optional) "refund" sub-object. If for example they break their API contract and fail to provide a "related_resources", you want to know. |
Folks: While implementing this I have encountered a small problem, to do with what happens if there are side-effects on the RHS of the optional operator. In normal method calls, as well as hash or array element lookups, the arguments are all evaluated in "left-to-right" order, as you would read the code. I have an implementation in which all of these are safe - nothing dies: undef?->method( die "method call" );
undef?->{die "hash elem"};
undef?->[die "array elem"]; But for calling subrefs, this isn't the case in normal perl: eval: my @ord; (do { push @ord, "inv"; sub {} })->(do { push @ord, "arg1"; 1 }, do { push @ord, "arg2"; 2 }); [@ord]
[ 'arg1', 'arg2', 'inv' ] This shows that the arguments are evaluated before the invocant, for undef?->(die "subref call") This throws an exception from the side-effects of evaluating the arguments before it got as far as evaluating the invocant. A potential fix for this would be to evaluate the expressions in a different order for To be honest it's rather a mess that existing Perl already works this way:
|
I feel that the best option would be to change the behaviour of |
@leonerd Thank you so much for all the time and effort you are putting into this. I think this is going to be a great addition to Perl and I miss it almost every time I code. I am following all the discussion regarding evaluation order on Perl/perl5#19997 and friends, and while I still strongly believe it is a bug and should be fixed, I wonder if we can compartmentalize and move forward with the implementation of What do you think? |
Yeah that seems fine. It affects function calls (both subrefs and method calls), but hash and arrayref access would be fine to begin with. We already have an RFC 0018, so we've allocated a new number 0021 for this. Please rename (and add to the document header) and then we should be good to go. |
Done! Thank you! |
I believe it's awaiting the "Yes do it" rubber-stamp. I don't really have any objections to doing that. If nobody else does perhaps we say "Go do it".? |
I should also clarify, nothing is stopping anyone having a go at starting implementing this. Feel free to go ahead :) I have a mostly-working hack at it via a CPAN module and |
The status of this RFC is "waiting for an implementation to be produced." |
there didn't seem to be much interest. |
(PSC continues to discuss this RFC, largely in the context of being blocked by Paul's comment here: #23 (comment) ) |
In status Implemented, so merged. |
No description provided.