Mailing List Archive

Pre-RFC: Optional Chaining
Hello, Porters!

I am super excited to bring you this pre-RFC feature request, and humbly
ask you to review and let me know whether it has what it takes to go
further up the RFC road.

I have tried my best to properly fill all fields from the RFC template even
though I understand this is just the very first stage. Still, I think it
would be a great addition to the language - I'd certainly use it everywhere.

Thank you!
garu

-----------------------8<-----------------------
# Optional Chaining

## Preamble

Author: Breno G. de Oliveira <garu@cpan.org>
Sponsor:
ID:
Status: Draft

## Abstract

This RFC proposes a new operator, `?->`, to indicate optional dereference
chains.

## Motivation

Chained dereferencing of nested data structures is quite common in Perl
programs. However, and specially due to Perl's dynamic type system, there
are
times when you must check whether the data exists, is defined and holds a
reference before you can dereference it, otherwise the call may trigger a
runtime exception.

The current syntax for these verifications can be quite long, becoming even
longer the more nested verifications need to be done, making it not only
harder to write and read, but also more prone to human error.

## Rationale

An "optional chaining" operator would let us access values located deep
within
a chain of conected references and fail gracefully without having to check
that each of them is valid. This should result in shorter, simpler and more
correct expressions whenever the value being accessed may be missing, or
when
exploring objects and data structures without guarantees of which
keys/methods/indexes/accessors are provided.

Equivalent solutions have been thoroughly validated by many other popular
programming languages, which already provide this feature going back since
at least 2015. It is sometimes also referred to as a "safe call", "null
safe",
"safe navigation" or "optional path" operator.

JavaScript, Kotlin, C#, Swift, TypeScript, Groovy, all provide "?.", which
behaves, to the best of my knowledge, exactly as proposed here. Ruby also
does it, but uses "&.". PHP does it with "?->" like what we're proposing.

Raku implements it as ".?" instead of "?." to denote that, in its case,
it is checking whether the invocant has the righthand side method, not
whether
the invocant itself is defined.

Rust provides the "?" operator to collapse Result objects into
either their Ok value or their Err value
(e.g. File::open("hello.txt")?.read_to_string(&mut s)?)
This behaves somewhat similarly to what is being proposed here.

Python, Java and C++, notably, do not provide it yet - though PEP505 has
been
proposed for the former.

Another noteworthy mention is Objective-C, in which every chained call
automatically short-circuits to NULL without requiring a special operator.

## Specification

The `?->` operator would behave exactly like the current dereference arrow
`->`, except that, instead of causing a runtime error if the lefthand side
is undefined or not a reference, it would shortcut the whole expression
to `undef` in scalar or void context, and an empty list `()` in list
context.

As with `->`, spaces would be allowed before and after the `?`, so
`$obj?->method` can be written as `$obj ?-> method` or even `$obj ? ->
method`.

The `?->` operator would interact with the exact same things that the `->`
operator does and should be completely interchangeable with it, albeit
providing the short-circuit.

## Backwards Compatibility

This would not conflict, to the best of my knowledge, with any other syntax
in Perl 5. All code with `?->` currently yields a compile time syntax error,
except inside interpolated strings (where `"$foo?->{bar}"` resolves to
showing the reference address followed by a literal `?->{bar}`) and regular
expressions (where `?` acts as a special character and `/$foo?/` gives no
warnings, though I am not entirely sure of its purpose). These would need to
be updated to handle optional chains, even though it should be pointed that
trying to use `undef` inside a string or a regexp already yields an
`Use of uninitialized value` runtime warning, and would continue to do so
with the optional chain operator.

Outside the interpreter, since this would be a new operator, I am not sure
how hard it would be for static tools and modules to incorporate it.
However,
considering it would of course go through the whole 'use experimental' and
'use feature' process, I expect it to be about as hard as it was for the
postfix dereference implementation on those same tools, and maybe that can
be
used as a ballpark estimate.

Finally, because it involves a special token (`?`), I don't think it is
possible to emulate it for earlier Perl versions via a module, unless it's a
source filter.

## Security Implications

None foreseen.

## Examples

Here are a few use case examples, with their current Perl 5 equivalent
in the comments:

no warnings 'experimental::optional_deref'; # or something.
use feature 'optional_deref';


$x = $obj?->method; # $x = ref $obj ? $obj->method : undef;
# or $x = $obj->method if ref $obj;

$x = $obj?->method?->other; # my $tmp = ref $obj ? $obj->method :
undef;
# then $x = $tmp->other if $tmp;
# Note that in this case we need a
temporary
# storage to avoid calling ->method twice.

$x = $href?->{somekey}; # $x = ref $href ? $href->{somekey} :
undef;

$x = $href?->{foo}?->{bar}; # $x = ref $href && ref $href->{foo}
# ? $href->{foo}{bar} : undef;

# NOTE: I'd rather write the statement below as
$href?->{foo}?{bar}?{baz}
# but it may be harder for the compiler (see "Open Issues" below):
if ($href?->{foo}?->{bar}?->{baz} == 42) # if ( ref $href
# && ref $href->{foo}
# && ref
$href->{foo}{bar}
# &&
$href->{foo}{bar}{baz}
# == 42)

$x = $subref?->(42); # $x = ref $subref ? $subref->(42) : undef;

$x = $a?->[3]?->[0]; # $x = ref $aref && ref $aref->[3] ?
$aref->[3][0]
# : undef;

# A notable exception that does not use 'ref':
$x = SomeNamespace?->new; # $x = %SomeNamespace:: ? SomeNamespace->new
# : undef;

# attribution would, of course, also be allowed:
$href?->{foo}?->[3] = 'OHAI!'; # $href->{foo}[3] = 'OHAI'
# if ref $href && ref $href->{foo};

# postfix dereferencing:
%x = $href?->%*; # ref $href ? $href->%* : undef;
$x = $aref?->$#*; # ref $aref ? $aref->$#* : undef;

my $aref; say foreach $aref?->@*; # no loop, no warnings.
# behaves the same as foreach
$aref->@*

@x = $aref?->@*; # @x is now (), not (undef). Equivalent to:
# @x = ref $aref ? $aref->@* : ()

$x =~ s/\d+/$href?->{foo}/e; # $x =~ s/\d+/ref $href ? $href->{foo}
# : undef/e;


## Prototype Implementation

I think it may be possible to implement this with a source filter, but
I have not attempted to do so.

## Future Scope

Future versions may be cleverer and do extra checks on the statement
considering both sides of the operator, thus making it even more useful.

For example, `$href?->{a}?->{b}?->[3]?->{d}` is, according to this proposal,
equivalent to:

ref $href
&& ref $href->{a}
&& ref $href->{a}{b}
&& ref $href->{a}{b}[3]
? $href->{a}{b}[3]{d} : undef;

But it could be made equivalent to something like:

ref $href eq 'HASH'
&& exists $href->{a}
&& ref $href->{a} eq 'HASH'
&& exists $href->{a}{b}
&& ref $href->{a}{b} eq 'ARRAY'
&& length(@{$href->{a}{b}}) >= 4
&& ref $href->{a}{b}[3] eq 'HASH'
&& exists $href->{a}{b}[3]{d}
? $href->{a}{b}[3]{d} : undef;

Similarly, `$x = $obj?->a?->b` could be made equivalent to something like:

my $tmp = ref $obj && blessed($obj) && $obj->can('a') ? $obj->a : undef;
$x = ref $tmp && blessed($tmp) && $tmp->can('b') ? $tmp->b : undef;

None of these potential future updates would interfere with the proposed
syntax, just with how far Perl would be willing and able to safeguard it.

Another potentially interesting area of scope would be to allow for defining
a custom default value other than `undef`. This could be achieved by
offering similar syntax to the ternary operator, but it goes beyond the
scope of this RFC.

## Rejected Ideas

I am unaware whether this feature has already been proposed for Perl 5
in the past.

We could, potentially, achieve the same result by making `undef` respond to
any calls as `undef`, much like what Objective-C does, so `$x->{a}[3]{b}`
becomes `undef->{a}[3]{b}` then `undef->[3]{b}` then `undef->{b}` then
`undef`. The problem with this approach is that it completely eliminates the
runtime exception of trying to use undef as a reference, which may not be
what the developer wants. It would, in fact, potentially create problems for
existing programs that count on that runtime error to happen. Instead, I
believe it's better to make it explicit.

I have considered going the path of Raku and proposing the operator as `->?`
instead of `?->` but having the `?` closer to the invocant feels more like
what developers may expect, not only for its similarities with
implementations
in other languages but also because it reads like a ternary `?:`, which may
make it easier to read and glance over. In other words, I believe `$x?->y`
reads more like the intended behavior, whereas `$x->?y`, to me, reads like
Perl dereferenced the invocant before the check. Finally, postderefs would
be
even harder to read, e.g.: `$aref->?$#*`.

I have also considered using other token(s) for this, but having the `->` to
signal the dereference is something all Perl 5 developers have come to
expect,
and the `?` is the obvious choice not just for its similarity with other
implementations, but because of its behavioral similarities with the ternary
operator.

## Open Issues

* Would we be able to unambiguously omit the arrow in chained references?

I think, at least for an initial implementation, the arrow needs to be
mandatory for optional chaining even in cases where the lone arrow can be
omitted (notably, chained hash/array references), since allowing `?`
followed by a lot of other tokens could, potentially, become ambiguous with
ternary ops. That said, full compatibility would be great, and I have not
investigated enough to prove said ambiguity. In other words, it would be
great
to be able to write `$x?->{foo}?{bar}?{baz}` instead of
`$x?->{foo}?->{bar}?->{baz}`. Maybe in a future version?

* Other identity values?

I believe short-circuiting to `undef` in scalar context and `()` in list
context is a good start. Still, there may be other interesting "identity"
values to be returned depending on the variable being dereferenced or maybe
even its context and surroundings, like for example short-circuiting to the
empty string when being interpolated. Those may not be so obvious (what is
the
identity value for a subref, or a globref?) and therefore it would need to
be
done in a case by case manner, very thoroughly considering each one in order
to keep the syntax consistent and predictable, not magical.

## References

*
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-
* https://kotlinlang.org/docs/null-safety.html#safe-calls
* http://www.groovy-lang.org/operators.html#_safe_navigation_operator
* https://wiki.php.net/rfc/nullsafe_operator
* https://www.python.org/dev/peps/pep-0505/
* https://docs.raku.org/language/operators#methodop_.?
* https://bugs.ruby-lang.org/issues/11537
* https://tc39.es/proposal-optional-chaining/
*
https://doc.rust-lang.org/std/result/index.html#the-question-mark-operator-
*
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html
* https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html

## Copyright

Copyright (C) 2021, Breno G. de Oliveira

This document and code and documentation within it may be used,
redistributed
and/or modified under the same terms as Perl itself.

----------------------->8-----------------------
Re: Pre-RFC: Optional Chaining [ In reply to ]
From tokenizer and grammar point of view it will be easier to use ->? syntax
you don't need to modify current toke.c, you will only need to alter
grammar, by adding "optquestionmark".
Re: Pre-RFC: Optional Chaining [ In reply to ]
I proposed something similar when I first joined this mailing list, which
was a new keyword "it" which would be an alias for the last thing found to
exist by the "exists" predicate, which IIRC I also somehow modified to not
autoviv intermediates.

As autovivifying intermediate data structure layers is a core part of Perl,
it seems like continuing to use the tried and true workarounds is the way
to go rather than trying to scratch this itch.

Sorry.

On the other hand, we do have support for foo($bar{baz}) not creating a
nonexistent $bar{baz} unless $_[0] gets assigned to within foo, so there's
that: it should be possible to fairly easily craft a new module that
exports a function, say "optichain", that you could call like so

my $find_without_autoviv = optichain( \%bar, baz=>blarf=> );

and that would set $find_without_autoviv to undef when $bar{baz} does not
already exist, without autovivving it.

it might look something like this:

sub optichain{
my $container = shift;
@_ or return $container;
ref $container or return $container;
my $index = shift;
if("$container" =~ /HASH/ ){
unshift @_ $container{$index} }
elsif("$container" =~ /ARRAY/){
unshift @_ $container[$index] }
else{ carp "please do something about support of things
like >>>$container<<< as optichain containers" };
goto &optichain; # look, professor! Tail recursion!
}


--
"Lay off that whiskey, and let that cocaine be!" -- Johnny Cash
Re: Pre-RFC: Optional Chaining [ In reply to ]
2021-10-29 2:39 breno <oainikusama@gmail.com> wrote:

> Hello, Porters!
>
> I am super excited to bring you this pre-RFC feature request, and humbly
> ask you to review and let me know whether it has what it takes to go
> further up the RFC road.
>
> I have tried my best to properly fill all fields from the RFC template
> even though I understand this is just the very first stage. Still, I think
> it would be a great addition to the language - I'd certainly use it
> everywhere.
>
> Thank you!
> garu
>
> -----------------------8<-----------------------
> # Optional Chaining
>
> ## Preamble
>
> Author: Breno G. de Oliveira <garu@cpan.org>
> Sponsor:
> ID:
> Status: Draft
>
> ## Abstract
>
> This RFC proposes a new operator, `?->`, to indicate optional dereference
> chains.
>
>
breno

Thank you for your posting.

Is it possible to start with a short conversation in Pre-RFC?

This is for reducing the cost of reading.

And it can be painful if a lot of writing is denied.
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Thu, 28 Oct 2021 14:39:15 -0300
breno <oainikusama@gmail.com> wrote:

> Hello, Porters!
>
> I am super excited to bring you this pre-RFC feature request, and humbly
> ask you to review and let me know whether it has what it takes to go
> further up the RFC road.
>
> I have tried my best to properly fill all fields from the RFC template even
> though I understand this is just the very first stage. Still, I think it
> would be a great addition to the language - I'd certainly use it everywhere.
>
> Thank you!
> garu

As described in our RFC repo's README[1], pre-RFCs are supposed to be
short, 4-paragraph pitches, *not* complete RFC drafts.

Leaving that aside, I generally support this proposal. However, I'm not
sure if it's a good idea to make the operator work with *defined*
non-references. I think it's something that is rarely needed in code
that follows good practices and it would often hide real bugs.

[1] - https://github.com/Perl/RFCs/blob/master/README.md
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Fri, 29 Oct 2021 02:49:55 +0200
Tomasz Konojacki <me@xenu.pl> wrote:

> Leaving that aside, I generally support this proposal. However, I'm not
> sure if it's a good idea to make the operator work with *defined*
> non-references. I think it's something that is rarely needed in code
> that follows good practices and it would often hide real bugs.

To clarify: the same goes for references of the wrong type. I think it
should only suppress the errors caused by undefined values.
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Thu, Oct 28, 2021 at 9:13 PM Yuki Kimoto <kimoto.yuki@gmail.com> wrote:

> Is it possible to start with a short conversation in Pre-RFC?
>
> This is for reducing the cost of reading.
>

Sure! Sorry for jumping the gun, my idea was to make things better, not
worse.

The tl;dr version is this: when you have little/no control over a
variable's content, a lot of repetitive and noisy boilerplate code is
required to fail gracefully (without runtime errors) while checking deep
chains of nested code like $data->{some}{deeply}{nested}[0]{content}
because, with strict and warnings, if any part of that chain is defined but
not a ref, you'll get a runtime exception. So if you want to check if that
value equals to 5 you do something like this:

if (ref $data eq 'HASH'
&& ref $data->{some} eq 'HASH'
&& ref $data->{some}{deeply} eq 'HASH'
&& ref $data->{some}{deeply}{nested} eq 'ARRAY'
&& ref $data->{some}{deeply}{nested}[0] eq 'HASH'
&& $data->{some}{deeply}{nested}[0]{content} == 5
) {
do_something()
}

My proposal is for a new "optional chain" operator, namely "?->", that
would fail gracefully whenever the lefthand side of the operator is not a
reference by returning undef in scalar context or an empty list ()
otherwise. So we could rewrite the code above as:

if ($data?->{some}?->{deeply}?->{nested}?->[0]?->{content} == 5) {
do_something();
}

Of course, you could write a scoped "no strict 'refs'" and keep the
original comparison, or wrap the code under eval/try, but maybe you *want*
part of that chain to yield a runtime exception. It gets even worse with
objects, because "no strict 'refs'" will not help you and fetching
something like SomeModule->new->this->then->that where any of those is
undef or does not provide the desired accessor will trigger a "Can't locate
object method X" runtime error. To check if that value equals to 5, for
example, you may need to do something like:

use Scalar::Util qw(blessed);
my $tmp = SomeModule->new;
if (blessed($tmp) && $tmp->can('this')) {
my $tmp2 = $tmp->this;
if (blessed($tmp2) && $tmp2->can('then')) {
my $tmp3 = $tmp2->then;
if (blessed($tmp3) && $tmp3->can('that') && $tmp3->that == 5) {
do_something()
}
}
}

Note that you need the temporary variables unless you're sure calling those
accessors more than once will not cause any side-effects.

With the proposed optional chaining operator, we could write the code above
as:

if (SomeModule?->new?->this?->then?->that == 5) {
do_something();
}

Again, you could wrap all that in an eval/try block, but it may be the case
where you only want to make the validation for certain parts of the chain,
and either way you'll have to check the error message to see if it was just
because the (sub)object wasn't there or because it crashed inside one of
the calls.

It's important to note this is not an original idea: many popular languages
like Rust, JavaScript, Kotlin, C#, PHP and Ruby already implement some form
of this since at least 2015.

Hope this is a more palatable version. Thank you again for taking the time
to review this and consider it for inclusion into the language.

Cheers!
garu
Re: Pre-RFC: Optional Chaining [ In reply to ]
On 2021-10-28 10:39 a.m., breno wrote:
> This RFC proposes a new operator, `?->`, to indicate optional dereference
> chains.

Thank you for addressing the bike-shedding question why ?-> rather than ->? in
your rejected ideas section.

From a language idiom consistency and semantics point of view, my biggest
potential issue is the distinction between definedness and truthiness, and what
the question mark conceptually better fits with, as fitting with both might not
be ideal.

-- Darren Duncan
Re: Pre-RFC: Optional Chaining [ In reply to ]
Op 28-10-2021 om 19:39 schreef breno:
> This RFC proposes a new operator, `?->`, to indicate optional dereference
> chains.


Yes, please!


>     # NOTE: I'd rather write the statement below as
> $href?->{foo}?{bar}?{baz}
>     # but it may be harder for the compiler (see "Open Issues" below):
>     if ($href?->{foo}?->{bar}?->{baz} == 42)   # if (   ref $href
>                                                # && ref $href->{foo}
>                                                # && ref $href->{foo}{bar}
>                                                # && $href->{foo}{bar}{baz}
>                                                #     == 42)


## Open Issues
>
> * Would we be able to unambiguously omit the arrow in chained references?
>
> I think, at least for an initial implementation, the arrow needs to be
> mandatory for optional chaining even in cases where the lone arrow can be
> omitted (notably, chained hash/array references), since allowing `?`
> followed by a lot of other tokens could, potentially, become ambiguous
> with
> ternary ops. That said, full compatibility would be great, and I have not
> investigated enough to prove said ambiguity. In other words, it would
> be great
> to be able to write `$x?->{foo}?{bar}?{baz}` instead of
> `$x?->{foo}?->{bar}?->{baz}`. Maybe in a future version?


Agree. I would probably not miss it. I find it ugly, where ?-> is elegant.


>
> * Other identity values?
>
> I believe short-circuiting to `undef` in scalar context and `()` in list
> context is a good start. Still, there may be other interesting "identity"
> values to be returned depending on the variable being dereferenced or
> maybe
> even its context and surroundings, like for example short-circuiting
> to the
> empty string when being interpolated. Those may not be so obvious
> (what is the
> identity value for a subref, or a globref?) and therefore it would
> need to be
> done in a case by case manner, very thoroughly considering each one in
> order
> to keep the syntax consistent and predictable, not magical.


Maybe $href?->{foo} : "default value"; ? Becomes unreadable, but
ternaries are unreadable anyway and I cannot think of a better alternative.


M4
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Thu, 28 Oct 2021 14:39:15 -0300
breno <oainikusama@gmail.com> wrote:

> Hello, Porters!
>
> I am super excited to bring you this pre-RFC feature request, and
> humbly ask you to review and let me know whether it has what it takes
> to go further up the RFC road.

Overall: a "pre-RFC" is supposed to be a paragraph or two of "should I
write this RFC?" as a simple yes/no question. What you've written here
is already the full RFC. :)

> Finally, because it involves a special token (`?`), I don't think it
> is possible to emulate it for earlier Perl versions via a module,
> unless it's a source filter.

XS::Parse::Infix could parse it, on a suitable perl build with the
PL_infix_plugin support. That would let you experiment with this as a
CPAN module first.

https://metacpan.org/pod/XS::Parse::Infix#DESCRIPTION

> ## Rejected Ideas
>
> I am unaware whether this feature has already been proposed for Perl 5
> in the past.
>
> We could, potentially, achieve the same result by making `undef`
> respond to any calls as `undef`, much like what Objective-C does, so
> `$x->{a}[3]{b}` becomes `undef->{a}[3]{b}` then `undef->[3]{b}` then
> `undef->{b}` then `undef`. The problem with this approach is that it
> completely eliminates the runtime exception of trying to use undef as
> a reference, which may not be what the developer wants. It would, in
> fact, potentially create problems for existing programs that count on
> that runtime error to happen. Instead, I believe it's better to make
> it explicit.

Definitely agree.

> I have considered going the path of Raku and proposing the operator
> as `->?` instead of `?->` but having the `?` closer to the invocant
> feels more like what developers may expect, not only for its
> similarities with implementations
> in other languages but also because it reads like a ternary `?:`,
> which may make it easier to read and glance over. In other words, I
> believe `$x?->y` reads more like the intended behavior, whereas
> `$x->?y`, to me, reads like Perl dereferenced the invocant before the
> check. Finally, postderefs would be
> even harder to read, e.g.: `$aref->?$#*`.

Likewise - given the positioning of the "?" there I would expect

$obj?->method # equivalent to $obj->method if defined $obj;

$obj->?method # equivalent to $obj->method if $obj->can("method")


--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Fri, 29 Oct 2021 at 18:35, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

> On Thu, 28 Oct 2021 14:39:15 -0300
> breno <oainikusama@gmail.com> wrote:
> > I have considered going the path of Raku and proposing the operator
> > as `->?` instead of `?->` but having the `?` closer to the invocant
> > feels more like what developers may expect, not only for its
> > similarities with implementations
> > in other languages but also because it reads like a ternary `?:`,
> > which may make it easier to read and glance over. In other words, I
> > believe `$x?->y` reads more like the intended behavior, whereas
> > `$x->?y`, to me, reads like Perl dereferenced the invocant before the
> > check. Finally, postderefs would be
> > even harder to read, e.g.: `$aref->?$#*`.
>
> Likewise - given the positioning of the "?" there I would expect
>
> $obj?->method # equivalent to $obj->method if defined $obj;
>
> $obj->?method # equivalent to $obj->method if $obj->can("method")
>

Raising this suggestion for completeness (I like the suggested `?->` as-is):

`//->` would be more consistent with our existing `//` defined-or operator,
since `? :` already sets the precedent that the `?` is checking for true,
rather than defined.

There is perhaps an argument for the `?->` operation to be _entirely_ based
on `ref`, allowing:

%x = (x => "example");
is($x{example}?->{this_is_ignored}, 'example');

The RFC suggests that instead this would return `undef`, which I think may
be a bit confusing?

but +1 for the overall concept from me - existing workarounds are
universally terrible.
Re: Pre-RFC: Optional Chaining [ In reply to ]
On 2021-10-29 4:44 a.m., Tom Molesworth via perl5-porters wrote:
> On Fri, 29 Oct 2021 at 18:35, Paul "LeoNerd" Evans wrote:
>
> Raising this suggestion for completeness (I like the suggested `?->` as-is):
>
> `//->` would be more consistent with our existing `//` defined-or operator,
> since `? :` already sets the precedent that the `?` is checking for true, rather
> than defined.

I was totally thinking this too though didn't say it in my other reply, that
using //-> would be more consistent with the existing defined-or operator, while
?-> speaks to booleans. -- Darren Duncan
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Thu, Oct 28, 2021, at 1:39 PM, breno wrote:
> Hello, Porters!

Hello! I will try to be brief.

> ## Specification
>
> The `?->` operator would behave exactly like the current dereference arrow
> `->`, except that, instead of causing a runtime error if the lefthand side
> is undefined or not a reference, it would shortcut the whole expression
> to `undef` in scalar or void context, and an empty list `()` in list context.

I think a few others have said: I think this needs to be based on defined, not ref. To throw another example, on the pile:

package Class {
use Moose;
has friend_factory => (is => 'ro', default => 'Friend' );
}

my $pal = Class->new->friend_factory?->new;

I want to be able to call methods on class names safely, too.

I am a little uncertain about the spelling, but don't have a better option *and* my concern is ridiculous: I like that Ruby allows predicate methods, ending in "?", and this would get in the way of that… if we made it legal. So maybe ignore that.

> As with `->`, spaces would be allowed before and after the `?`, so
> `$obj?->method` can be written as `$obj ?-> method` or even `$obj ? -> method`.

This feels somewhat perverse. Why is ?-> not a single and inseparable token?

The other concern I have is with string interpolation. "postfix_qq" is still a bit of a pain and feels like a mistake. Options include, at least:
* just declare `"$x?->{foo}"` acts like `"" . $x?->{foo}` and we don't worry about the backcompat, as we assume it's wildly unlikely
* the same, but we always keep this behind a feature flag so everyone has opted in
* we find a syntax that is not presently legal instead
* we do not allow this in interpolation, allowing it to work someday via something like JavaScript template literals, to be invented at a future date
The second seems tolerable. I would love to have the thing imagined in the 4th, but not keen to make ?-> contingent on that.

--
rjbs
Re: Pre-RFC: Optional Chaining [ In reply to ]
On 28-Oct-21 19:39, breno wrote:
>
> * Would we be able to unambiguously omit the arrow in chained references?
>
> I think, at least for an initial implementation, the arrow needs to be
> mandatory for optional chaining even in cases where the lone arrow can be
> omitted (notably, chained hash/array references), since allowing `?`
> followed by a lot of other tokens could, potentially, become ambiguous
> with
> ternary ops. That said, full compatibility would be great, and I have not
> investigated enough to prove said ambiguity. In other words, it would
> be great
> to be able to write `$x?->{foo}?{bar}?{baz}` instead of
> `$x?->{foo}?->{bar}?->{baz}`. Maybe in a future version?
>
How about this?

Let
    $foo?->{a}{b}{c}
mean
    $foo?->{a}?->{b}?->{c}
while
    $foo->{a}{b}{c}
continues to mean
    $foo->{a}->{b}->{c}

In other words, an omitted arrow in a chained reference is optional or
not depending on the previous explicit arrow, -> or ?-> .

If you need to, you can still write
    $foo?->{a}{b}->{c}{d}
if you need
    $foo?->{a}?->{b}->{c}->{d}
or
    $foo->{a}{b}?->{c}->{d}
if you want
    $foo->{a}->{b}?->{c}->{d}

This should be backwards compatible, and seems dwimmy enough to me.

 – Michael
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sun, Oct 31, 2021, at 9:31 AM, Michael Schaap wrote:
> In other words, an omitted arrow in a chained reference is optional or
> not depending on the previous explicit arrow, -> or ?-> .

I think this would be a mistake for at least three reasons:

1. It would not match the behavior of other popular versions of this operator, like JavaScript's. Why surprise people?

2. It would mean that these two pieces of code would be different, which I, at least, think is confusing and complicates refactoring:
# FIRST
$x?->{a}->{b}->{c};

# SECOND
$y = $x?->{a};
$y->{b}->{c};

3. It means that although we're adding a new operator that lets you pick between "safe" or "unsafe" arrow within an expression, we can only choose "safe" *once* before the choice is taken away for the rest of the expression. It's less powerful.

--
rjbs
Re: Re: Pre-RFC: Optional Chaining [ In reply to ]
On 31-Oct-21 15:10, Ricardo Signes wrote:
> On Sun, Oct 31, 2021, at 9:31 AM, Michael Schaap wrote:
>> In other words, an omitted arrow in a chained reference is optional or
>> not depending on the previous explicit arrow, -> or ?-> .
>
> I think this would be a mistake for at least three reasons:
>
> 1. It would not match the behavior of other popular versions of this
> operator, like JavaScript's.  Why surprise people?
Are you sure?
JavaScript , as far as I know, doesn't /have/ a way to omit the . or ?.
in chained references.
(I don't know if other languages do, and if so, how they handle it.)

>
> 2. It would mean that these two pieces of code would be different,
> which I, at least, think is confusing and complicates refactoring:
> # FIRST
> $x?->{a}->{b}->{c};
>
> # SECOND
> $y = $x?->{a};
> $y->{b}->{c};
>
No, these would be the same.  If you do include an arrow, they would
indicate optional (?->) or non-optional(->) for the next argument(s).
In this case, though, the code would have a different meaning:

# FIRST
$x?->{a}{b}{c};

# SECOND
$y = $x?->{a};
$y{b}{c};


and you do have a point that this may be confusing and complicates
refactoring.


> 3. It means that although we're adding a new operator that lets you
> pick between "safe" or "unsafe" arrow within an expression, we can
> only choose "safe" /once/ before the choice is taken away for the rest
> of the expression. It's less powerful.
>
No, you can always use $x?->{a}{b}->{c}{d}?->{e}{f} to switch between
optional and not optional.  (I'm not sure which one you consider
"safe".  :-) )

 – Michael
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sun, Oct 31, 2021, at 10:31 AM, Michael Schaap wrote:
> On 31-Oct-21 15:10, Ricardo Signes wrote:
>> On Sun, Oct 31, 2021, at 9:31 AM, Michael Schaap wrote:
>>> In other words, an omitted arrow in a chained reference is optional or
>>> not depending on the previous explicit arrow, -> or ?-> .
>>
>> I think this would be a mistake for at least three reasons:
>>
>> 1. It would not match the behavior of other popular versions of this operator, like JavaScript's. Why surprise people?
> Are you sure?
> JavaScript , as far as I know, doesn't *have* a way to omit the . or ?. in chained references.
> (I don't know if other languages do, and if so, how they handle it.)

I see. I had misunderstood that you were talking about *omitted* arrows. I think making the behavior of the implicit arrow different is *also* a bad idea, largely for the "refactoring becomes more complex" argument I provided.

--
rjbs
Re: Pre-RFC: Optional Chaining [ In reply to ]
Language consistency issue

why only operator -> should be affected? Why excluding other operators?
(eg: =~)

it would be nice to write eg:
```
my ($bar, $baz) = $foo =~? m/.../
```

instead of
```
my ($bar, $baz);
($bar, $baz) = $foo =~ m/.../ if defined $foo;

Extrapolated to any operator: there should be mechanism (similar to SQL
NULL handling) to say "if undefined here, result of operation is undef", eg?
```
$foo +? $bar
$foo ?+ $bar
$foo ?+? $bar
$foo++?

Question: are optional expressions evaluated?
Example:
```
$counter = 0;
$foo->?[$counter++]->?{$counter++}->?bar ($counter++);
```
What will be value of $counter?

Brano
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sun, Oct 31, 2021, at 4:59 PM, Branislav Zahradník wrote:
> Language consistency issue
>
> why only operator -> should be affected? Why excluding other operators? (eg: =~)

Because this part of the problem is tractable and familiar.

We have a generic mechanism for doing operation X to $foo iff $foo is defined:

defined $foo ? X($foo) : ()

?-> is sugar for one very specific case, which can be considered pretty closely.

For something more generic, what's the underlying principle? "All binary operators can be prefixed with a question mark to short circuit."? (You wrote "any" operator in part of your message.)

Surely not the ".." operator, though, right? Can you use the "?//" operator? What about "?=="?

The other thing you cited is SQL NULL. In SQL, the language has a deeply-baked ternary logic system that Perl just doesn't. It feels like a big lift.

The big difference here is that one often uses -> to drill down through a series of references. "?+" isn't particularly useful. In arithmetic code, you might write "$x + $y / $z % $w". If any of these is undef and you believe this should void the function, you'd use a ternary or other boolean logic to just check their definedness. You're not likely to say "$x ?+ $y / $z ?% $w" unless you're being exceptionally clever, and *also*, addition is a commutative operation, so is ?+ checking both sides?

I think ?-> makes sense. ?=~ is plausible, but I think already much less likely to be useful. ?=~ can be chained with s///r, but usually is not, and in other cases, using a ternary is probably simpler.

As for everything else, I think those roads leads to madness.

--
rjbs
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sun, Oct 31, 2021 at 06:04:24PM -0400, Ricardo Signes wrote:
> On Sun, Oct 31, 2021, at 4:59 PM, Branislav Zahradn?k wrote:
> > Language consistency issue
> >
> > why only operator -> should be affected? Why excluding other operators? (eg: =~)
>
> The other thing you cited is SQL NULL. In SQL, the language has a deeply-baked ternary logic system that Perl just doesn't. It feels like a big lift.
>
> The big difference here is that one often uses -> to drill down through a series of references. "?+" isn't particularly useful. In arithmetic code, you might write "$x + $y / $z % $w". If any of these is undef and you believe this should void the function, you'd use a ternary or other boolean logic to just check their definedness. You're not likely to say "$x ?+ $y / $z ?% $w" unless you're being exceptionally clever, and *also*, addition is a commutative operation, so is ?+ checking both sides?

You could also do:

no warnings "uninitialized";

if you don't care about whether this follows SQL NULL rules, but this can't work for ?->

Tony
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Thu, Oct 28, 2021 at 08:21:53PM +0200, Branislav Zahradn?k wrote:
> >From tokenizer and grammar point of view it will be easier to use ->? syntax
> you don't need to modify current toke.c, you will only need to alter
> grammar, by adding "optquestionmark".

The change to toke.c would be close to trivial.

I'm not sure you could avoid allowing '->?' to be spelled as '-> ?' if you did
this in the parser, I haven't looked closely.

Tony
Re: Pre-RFC: Optional Chaining [ In reply to ]
The other thing you cited is SQL NULL. In SQL, the language has a
> deeply-baked ternary logic system that Perl just doesn't. It feels like a
> big lift.
>
>
That was my point, this pre-RFC defines it (ternary logic system) but only
in small part.


> The big difference here is that one often uses -> to drill down through a
> series of references.
>

"drill down" sounds to me like a data query language


>
>
> As for everything else, I think those roads leads to madness.
>
>
Inconsistency and unclear/unexpected behaviour leads to madness.
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sun, 31 Oct 2021 at 23:36, Tony Cook <tony@develop-help.com> wrote:

> On Thu, Oct 28, 2021 at 08:21:53PM +0200, Branislav Zahradník wrote:
> > >From tokenizer and grammar point of view it will be easier to use ->?
> syntax
> > you don't need to modify current toke.c, you will only need to alter
> > grammar, by adding "optquestionmark".
>
> The change to toke.c would be close to trivial.
>
> I'm not sure you could avoid allowing '->?' to be spelled as '-> ?' if
> you did
> this in the parser, I haven't looked closely.
>

you can but you need to have two to tokens:
question_mark: QUESTION_MARK | QUESTION_MARK_AFTER_WHITE




>
> Tony
>
Re: Pre-RFC: Optional Chaining [ In reply to ]
Sorry for coming late to the game ...

On Fri, Oct 29, 2021 at 07:44:22PM +0800, Tom Molesworth via perl5-porters wrote:
> On Fri, 29 Oct 2021 at 18:35, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
> > Likewise - given the positioning of the "?" there I would expect
> > $obj?->method # equivalent to $obj->method if defined $obj;
> > $obj->?method # equivalent to $obj->method if $obj->can("method")
>
> Raising this suggestion for completeness (I like the suggested `?->` as-is):
>
> `//->` would be more consistent with our existing `//` defined-or operator,
> since `? :` already sets the precedent that the `?` is checking for true,
> rather than defined.

See also my proposal a while back for a defined-match operator, in
<20190221115541.GB11111@bytemark.barnyard.co.uk>.

--
David Cantrell | Reality Engineer, Ministry of Information

More people are driven insane through religious hysteria than
by drinking alcohol. -- W C Fields
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Thu, Oct 28, 2021, at 13:39, breno wrote:
> I am super excited to bring you this pre-RFC feature request, and humbly ask you to review and let me know whether it has what it takes to go further up the RFC road.

This morning in the PSC's weekly call, we were discussing this proposal and its status. I said I'd write something back to the list.

Having re-read the posts, I think there was a lot of work in the weeds, and the pre-RFC question sounds like "yes of course people would like something like this," and then we get to the actual RFC question. But Breno's pre-RFC was basically an RFC and could be filed as such, then to be worked on until ready for someone to have a crack at implementing.

My comments on the PSC call this morning were mostly that I felt the most valuable simplification would be to provide an explanation of ?-> in terms of what it's equivalent to. For example:

EXPR1 ?-> EXPR2

# is equivalent to

defined EXPR1 ? EXPR1->EXPR2 : undef

# with the caveat that EXPR1 is only evaluated once

I think the objection (made by a few, including me) that we need to use "defined" (as I do in the snippet above) rather than "ref" as the test should be taken into account in any re-submission of the RFC.

But is there anything left to do but…
1. submit the RFC in an updated form
2. discuss whether it's ready to implement in that form
3. eventually say it's ready for implementing
?

--
rjbs
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Fri, Jun 3, 2022 at 5:38 PM Ricardo Signes <perl.p5p@rjbs.manxome.org>
wrote:

> Having re-read the posts, I think there was a lot of work in the weeds,
> and the pre-RFC question sounds like "yes of course people would like
> something like this," and then we get to the actual RFC question. But
> Breno's pre-RFC was basically an RFC and could be filed as such, then to be
> worked on until ready for someone to have a crack at implementing.
>

That's terrific, thank you all for considering this proposition!

My comments on the PSC call this morning were mostly that I felt the most
> valuable simplification would be to provide an explanation of ?-> in terms
> of what it's equivalent to. For example:
>
> EXPR1 ?-> EXPR2
>
> # is equivalent to
>
> defined EXPR1 ? EXPR1->EXPR2 : undef
>
> # with the caveat that EXPR1 is only evaluated once
>
>
Perfect, except I think it really should address the empty list case by
returning () (which becomes undef in scalar context) instead of always
returning plain undef:

defined EXPR1 ? EXPR1->EXPR2 : ()

So we can also write:

push @list, $item?->foo; # does NOT add undef to @list when $item is
undef
@list = $arrayref?->@*; # if $arrayref is undef, @list stays empty
and not (undef)

I think the objection (made by a few, including me) that we need to use
> "defined" (as I do in the snippet above) rather than "ref" as the test
> should be taken into account in any re-submission of the RFC.
>

I agree with the objection and I am very much ok with it using "defined"
instead of "ref", and volunteer to work on an updated version of the
original document, to be resubmitted as an exploratory RFC back to the
group or as a PR, at the PSC's discretion.

>

>
> But is there anything left to do but…
>
> 1. submit the RFC in an updated form
> 2. discuss whether it's ready to implement in that form
> 3. eventually say it's ready for implementing
>
> ?
>

I'd like to address some observations made before regarding the proposal,
but I'll do it in a separate email.

Cheers!
garu
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Fri, Jun 3, 2022 at 5:38 PM Ricardo Signes <perl.p5p@rjbs.manxome.org>
wrote:

> But is there anything left to do but…
>
> 1. submit the RFC in an updated form
> 2. discuss whether it's ready to implement in that form
> 3. eventually say it's ready for implementing
>
> ?
>

Some comments on questions that arose in the past:

* why just the deref arrow -> and not other tokens/operators (e.g. ?=~, ?=,
etc)

Because it's trying to solve the specific issue of having to test an entire
chain individually instead of just one value. There is prior literature on
languages that have gone the extra mile on this and it was considered more
trouble than benefit, and is one of the reasons PEP505 was still not
accepted - it tries to do too much.

* are optional expressions evaluated? e.g. $y = $x?->{$i++}?->[++$i]

I think the operator should short-circuit and not evaluate any expressions
further up the chain. Like what would happen if you replaced that with a
lot of &&, ternaries or if() clauses.

* is $foo?->{x}{y}{z} the same as $foo?->{x}?->{y}?->{z} ?

No. $foo?->{x}{y}{z} means $foo?->{x}->{y}->{z}. To optional-chain
everything you would have to be explicit. The reason behind this is you may
want to get the original error/warning/vivification somewhere down the
chain.

* how would it handle things like $foo?->{x}?->{y} = 42

Same as $foo->{x}{y} = 42 if defined $foo && defined $foo->{x} && defined
$foo->{x}{y} (except way more readable)

* will string interpolation work? e.g. "$foo?->{x} and $bar?->y()"

We could learn from postderef_qq and decide what to do. Rik's suggestion of
hiding it behind a feature flag (at least for now) feels right to me.
However, since it defaults to undef, it would still trigger a warning, so
maybe it could pick the empty string as fallback for strings. This would
DWIM but it may not be that easy to implement or to predict, and
consistency would demand for it to return 0 on $foo?->{x} + 42 and to
return 1 on $foo?->{x} * 42, and who knows where else. Better to keep
things simple and predictable, even if it means you cannot use it in string
interpolation unless you cheat with something like "value is ${
\$foo?->{some}?->{value} }".

If we decide we don't care if it breaks existing strings, it should be
noted this construct seems pretty rare anyway: querying for "?->" on
grep.metacpan shows nothing but comments, pod and regexes.

* speaking of regexes, should /$foo?->x?->y/ or /$foo?->{x}?->{y}/ work?

grep.metacpan doesn't show any /$var?->/ constructs as they make little
sense since "?" is a metacharacter, but a few regexes have something like
/.*?->/. I think the parser should be able to tell when it's a regex
modifier or not, or when it follows a variable or not, but if this gets too
confusing or complicated it can be hidden inside a feature flag or
disallowed entirely.

* is \$foo?->x equal to (\$foo)?->x or to \($foo?->x)

The latter, just like \$foo->x is.

* should it be a CPAN module first?

As LeoNerd pointed out, XS::Parse::Infix could parse it. But I would only
give it a go if p5p felt there really is a need to make it a CPAN module
first.

* definedness vs truthfulness

Some pointed "?->" may be associated with truthfulness due to the ternary
operator "?:", whereas "//" could be more appropriate as it associates with
definedness, which is what we want. I agree that any confusion should be
avoided if possible, and that it may well be the case here. My issue with
"//->" is that "//" means defined-or, in the sense of the righthand-side
being called only if the lefthand-side is undef, which is the opposite of
what we want to achieve with "//->", and that can also lead to confusion.
So I'm not sure here. Perl uses a lot of symbols for a lot of things, and
my guess is each candidate will either be too obscure or also ambiguous
(like ~> or &->). At least with "?->" it will be familiar enough for
perlers and people coming from other languages.

Cheers!
garu
Re: Pre-RFC: Optional Chaining [ In reply to ]
Hi there,

On Sat, 4 Jun 2022, breno wrote:

> ...
> * how would it handle things like $foo?->{x}?->{y} = 42
>
> Same as $foo->{x}{y} = 42 if defined $foo && defined $foo->{x} && defined
> $foo->{x}{y} (except way more readable)
> ...

Run that one by me again?

--

73,
Ged.
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sat, Jun 4, 2022 at 5:13 AM breno <oainikusama@gmail.com> wrote:

breno,

First, thanks for your magnificently detailed and patient explanations. I'm
quite impressed! I've long wanted this feature for Perl and I'd be
delighted to see this feature implemented.

* definedness vs truthfulness
>
> Some pointed "?->" may be associated with truthfulness due to the ternary
> operator "?:", whereas "//" could be more appropriate as it associates with
> definedness,
>

The fact that "?" is used in several other languages for this construct is
compelling to me. More to the point, if we're going to go to YAPC (Yet
Another Punctuation Character"), at least introduce one that would read
naturally to users coming from other languages. Thus, "?->" feels both
natural and appropriate to me.

Best,
Ovid
Re: Pre-RFC: Optional Chaining [ In reply to ]
* breno <oainikusama@gmail.com> [2022-06-04 00:04:36 -0300]:

> On Fri, Jun 3, 2022 at 5:38 PM Ricardo Signes <perl.p5p@rjbs.manxome.org>
> wrote:
>
> > Having re-read the posts, I think there was a lot of work in the weeds,
> > and the pre-RFC question sounds like "yes of course people would like
> > something like this," and then we get to the actual RFC question. But
> > Breno's pre-RFC was basically an RFC and could be filed as such, then to be
> > worked on until ready for someone to have a crack at implementing.
> >
>
> That's terrific, thank you all for considering this proposition!

Idk why all the static about it being to well developed. I think it's great
and shows a lot of thought. A pre-RFC should be the seed from which the RFC
springs; so why not show up with something that's a little more mature? I
think root of the complaint is "give me the executive summary before I spend
time reading though this" - which I think is fair. But if that was part of
it who cares how long it is?

>
> My comments on the PSC call this morning were mostly that I felt the most
> > valuable simplification would be to provide an explanation of ?-> in terms
> > of what it's equivalent to. For example:
> >
> > EXPR1 ?-> EXPR2
> >
> > # is equivalent to
> >
> > defined EXPR1 ? EXPR1->EXPR2 : undef
> >
> > # with the caveat that EXPR1 is only evaluated once

I like this, but what distinction does "defined" or "truthy"
hold with references?

It seems necessary for this to even be "true", the following
conditions must hold:

* it's not undef
* it's blessed
* it "can" EXPR2

> >
> >
> Perfect, except I think it really should address the empty list case by
> returning () (which becomes undef in scalar context) instead of always
> returning plain undef:
>
> defined EXPR1 ? EXPR1->EXPR2 : ()
>
> So we can also write:
>
> push @list, $item?->foo; # does NOT add undef to @list when $item is
> undef

Seems like this suggests a failure of $item?->foo throws an exception so
is there some implicit try/catch here or is "push" supposed to now know
about this; or is it more like this:

push @list, eval { $item?->foo }; # which doesn't fulfill the "don't add ..."

But maybe more like this is the desired behavior?

local $@;
my $added_ok = eval { push @list, $item?->foo }
# now maybe check if $added_ok or if $@ has something in it?

So I think that while useful, ->? way necessarily need to return "undef", but I still
think this is useful if you have a single step to filter out the undefs.

Or maybe something like this, that would collect anything that has no reachable
"wang" routine for whatever reason gets collectde into the 'unknown' key:

my $bucket = {};
foreach my $thing (@stuff) {
push @{$bucket=>{$thing->?wang} // q{unknown}}, $thing;
}

> @list = $arrayref?->@*; # if $arrayref is undef, @list stays empty
> and not (undef)

Again, this is cutting across a lot of things knowing what the failure
mode of "?->" is and doing something reasonable with it.

>
> I think the objection (made by a few, including me) that we need to use
> > "defined" (as I do in the snippet above) rather than "ref" as the test
> > should be taken into account in any re-submission of the RFC.
> >
>
> I agree with the objection and I am very much ok with it using "defined"
> instead of "ref", and volunteer to work on an updated version of the
> original document, to be resubmitted as an exploratory RFC back to the
> group or as a PR, at the PSC's discretion.
>
> >
>
> >
> > But is there anything left to do but?
> >
> > 1. submit the RFC in an updated form
> > 2. discuss whether it's ready to implement in that form
> > 3. eventually say it's ready for implementing

4. implement it and sheperd it for the next 20 years.

And just to be clear, I think a "?->" that does something to help to create or
improve existing idioms is great. What it actually does, that's the point
of the RFC process, which basically is what everyone else has said..

Cheers,
Brett

> >
> > ?
> >
>
> I'd like to address some observations made before regarding the proposal,
> but I'll do it in a separate email.
>
> Cheers!
> garu

--
--
oodler@cpan.org
oodler577@sdf-eu.org
SDF-EU Public Access UNIX System - http://sdfeu.org
irc.perl.org #openmp #pdl #native
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Fri, Jun 3, 2022, at 23:13, breno wrote:
> Some comments on questions that arose in the past:

First: I agree, () was more correct than undef.

> * are optional expressions evaluated? e.g. $y = $x?->{$i++}?->[++$i]
>
> I think the operator should short-circuit and not evaluate any expressions further up the chain. Like what would happen if you replaced that with a lot of &&, ternaries or if() clauses.

Agreed, as is explained by the transformative explanation:

$x?->{$i++}?->[++$i]

# equivalent, *modulo multiple-evaluation*, to

defined $x ? (defined $x->{$i++} ? $x->{*$i++*}[++$i] : ()) : ()

The multiple-evaluation waiving is tricker above, but I think it's clear if we remember that repeated $i++, which I rendered in red, evaluates to the same value as the earlier $i++, with no side effect.

> * is $foo?->{x}{y}{z} the same as $foo?->{x}?->{y}?->{z} ?
>
> No. $foo?->{x}{y}{z} means $foo?->{x}->{y}->{z}. To optional-chain everything you would have to be explicit. The reason behind this is you may want to get the original error/warning/vivification somewhere down the chain.

Agreed.

> * how would it handle things like $foo?->{x}?->{y} = 42
>
> Same as $foo->{x}{y} = 42 if defined $foo && defined $foo->{x} && defined $foo->{x}{y} (except way more readable)

Good example, should become a test.

> * will string interpolation work? e.g. "$foo?->{x} and $bar?->y()"

Well, the second one surely would not, since you can't interpolate a method call. Probably we should not have it interpolate, since the undef would be a warning anyway. We can fix this by someday adding something like template strings, where whole expressions are marked off inside the string, rather than auto-detected.

> * speaking of regexes, should /$foo?->x?->y/ or /$foo?->{x}?->{y}/ work?

/${foo}?/ already means "0 or 1 instance of the pattern in $foo", so I think making ?-> work in there is no good.

> * is \$foo?->x equal to (\$foo)?->x or to \($foo?->x)

?-> should have the same precedence as ->

> * should it be a CPAN module first?
>
> As LeoNerd pointed out, XS::Parse::Infix could parse it. But I would only give it a go if p5p felt there really is a need to make it a CPAN module first.

Hey, LeoNerd, is this an afternoon to do with XS::Parse::Infix, or a month? :)

> * definedness vs truthfulness
>
> Some pointed "?->" may be associated with truthfulness due to the ternary operator "?:", whereas "//" could be more appropriate as it associates with definedness, which is what we want. I agree that any confusion should be avoided if possible, and that it may well be the case here.

If somebody has a really great argument for something better than ?-> I am interested, but "? means truth" seems too thin, and also garu's argument about the inverted sense of // is a good one, imho.

--
rjbs
Re: Pre-RFC: Optional Chaining [ In reply to ]
On 5/6/22 1:15, Oodler 577 via perl5-porters wrote:
> * breno <oainikusama@gmail.com> [2022-06-04 00:04:36 -0300]:
>

>>
>> My comments on the PSC call this morning were mostly that I felt the most
>>> valuable simplification would be to provide an explanation of ?-> in terms
>>> of what it's equivalent to. For example:
>>>
>>> EXPR1 ?-> EXPR2
>>>
>>> # is equivalent to
>>>
>>> defined EXPR1 ? EXPR1->EXPR2 : undef
>>>
>>> # with the caveat that EXPR1 is only evaluated once
>
> I like this, but what distinction does "defined" or "truthy"
> hold with references?
>
> It seems necessary for this to even be "true", the following
> conditions must hold:
>
> * it's not undef
> * it's blessed
> * it "can" EXPR2


Don't forget that perl allows one to call methods on strings as in...

$class = 'Foo';
$class->new();

So, "?->" requiring a reference there would be inconsistent.
Re: Pre-RFC: Optional Chaining [ In reply to ]
Op 05-06-2022 om 01:15 schreef Oodler 577 via perl5-porters:
> * breno <oainikusama@gmail.com> [2022-06-04 00:04:36 -0300]:
>
>> On Fri, Jun 3, 2022 at 5:38 PM Ricardo Signes <perl.p5p@rjbs.manxome.org>
>> wrote:
>>
>>> Having re-read the posts, I think there was a lot of work in the weeds,
>>> and the pre-RFC question sounds like "yes of course people would like
>>> something like this," and then we get to the actual RFC question. But
>>> Breno's pre-RFC was basically an RFC and could be filed as such, then to be
>>> worked on until ready for someone to have a crack at implementing.
>>>
>> That's terrific, thank you all for considering this proposition!
> Idk why all the static about it being to well developed. I think it's great
> and shows a lot of thought. A pre-RFC should be the seed from which the RFC
> springs; so why not show up with something that's a little more mature? I
> think root of the complaint is "give me the executive summary before I spend
> time reading though this" - which I think is fair. But if that was part of
> it who cares how long it is?
>
>> My comments on the PSC call this morning were mostly that I felt the most
>>> valuable simplification would be to provide an explanation of ?-> in terms
>>> of what it's equivalent to. For example:
>>>
>>> EXPR1 ?-> EXPR2
>>>
>>> # is equivalent to
>>>
>>> defined EXPR1 ? EXPR1->EXPR2 : undef
>>>
>>> # with the caveat that EXPR1 is only evaluated once
> I like this, but what distinction does "defined" or "truthy"
> hold with references?
>
> It seems necessary for this to even be "true", the following
> conditions must hold:
>
> * it's not undef
> * it's blessed
> * it "can" EXPR2


I'ld say, it should be exactly as garu/breno described, exactly his
equivalent. It returns undef unless EXPR1 is defined, else it
dereferences. Which may give errors and/or warnings, exactly as the
equivalent statement.


>>>
>> Perfect, except I think it really should address the empty list case by
>> returning () (which becomes undef in scalar context) instead of always
>> returning plain undef:
>>
>> defined EXPR1 ? EXPR1->EXPR2 : ()
>>
>> So we can also write:
>>
>> push @list, $item?->foo; # does NOT add undef to @list when $item is
>> undef
> Seems like this suggests a failure of $item?->foo throws an exception so
> is there some implicit try/catch here or is "push" supposed to now know
> about this; or is it more like this:


No, as I understand it, it is just scalar (return undef) versus list
context (return empty list). Makes a lot of sense.


HTH,

M4
Re: Pre-RFC: Optional Chaining [ In reply to ]
Op 05-06-2022 om 02:22 schreef Ricardo Signes:

> On Fri, Jun 3, 2022, at 23:13, breno wrote:
>
...

>> * speaking of regexes, should /$foo?->x?->y/ or /$foo?->{x}?->{y}/ work?
>
> /${foo}?/ already means "0 or 1 instance of the pattern in $foo", so I
> think making ?-> work in there is no good.


So having a different syntax that does work in regexes would make sense.
(But that might not be //-> either, how to parse "$x=~/$foo//->{x}/")?


HTH,

M4
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Thu, Oct 28, 2021, at 13:39, breno wrote:
> I am super excited to bring you this pre-RFC feature request, and humbly ask you to review and let me know whether it has what it takes to go further up the RFC road.

Breno,

I'm sorry, I thought I'd written this email but I don't see it. So if it's late, or if it's a duplicate, I apologize!

Could you please file this as a "full" RFC <https://github.com/Perl/RFCs/tree/main/rfcs> with the changes we've discussed? I think there are bits to hash out, but that we should get the document in place. Your original email should be a great starting point, with not too much editing required!

Thanks.

--
rjbs
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sat, Jun 4, 2022 at 4:16 PM Oodler 577 via perl5-porters <
perl5-porters@perl.org> wrote:

> I like this, but what distinction does "defined" or "truthy"
> hold with references?
>
> It seems necessary for this to even be "true", the following
> conditions must hold:
>
> * it's not undef
> * it's blessed
> * it "can" EXPR2
>

For $foo?->{bar}?->{baz}, nothing needs to be blessed. Watering this
feature down to only supporting method dispatch would be very disappointing.

These should all be possible too:

$foo?->*
$foo?->[0]
$foo?->@*
$foo?->%*
$foo?->()
Re: Pre-RFC: Optional Chaining [ In reply to ]
Dear porters,

RFC 0018 is already submitted as a PR: https://github.com/Perl/RFCs/pull/23

If anyone feels its content misrepresents or misses anything that needs to
be discussed before it gets accepted/rejected, please let me know. As
instructed in another thread, we should try and keep conversations here and
not on github.

Looking forward to the next steps!

Cheers,
garu

On Fri, Jun 17, 2022 at 9:59 AM Ricardo Signes <perl.p5p@rjbs.manxome.org>
wrote:

> On Thu, Oct 28, 2021, at 13:39, breno wrote:
>
> I am super excited to bring you this pre-RFC feature request, and humbly
> ask you to review and let me know whether it has what it takes to go
> further up the RFC road.
>
>
> Breno,
>
> I'm sorry, I thought I'd written this email but I don't see it. So if
> it's late, or if it's a duplicate, I apologize!
>
> Could you please file this as a "full" RFC
> <https://github.com/Perl/RFCs/tree/main/rfcs> with the changes we've
> discussed? I think there are bits to hash out, but that we should get the
> document in place. Your original email should be a great starting point,
> with not too much editing required!
>
> Thanks.
>
> --
> rjbs
>
Re: Pre-RFC: Optional Chaining [ In reply to ]
2022-7-13 14:00 breno <oainikusama@gmail.com> wrote:

> Dear porters,
>
> RFC 0018 is already submitted as a PR:
> https://github.com/Perl/RFCs/pull/23
>
> If anyone feels its content misrepresents or misses anything that needs to
> be discussed before it gets accepted/rejected, please let me know. As
> instructed in another thread, we should try and keep conversations here and
> not on github.
>
> Looking forward to the next steps!
>
>
It looks good!
Re: Pre-RFC: Optional Chaining [ In reply to ]
On 2022-07-12 11:09 p.m., Yuki Kimoto wrote:
> 2022-7-13 14:00 breno wrote:
> Dear porters,
>
> RFC 0018 is already submitted as a PR: https://github.com/Perl/RFCs/pull/23
>
> If anyone feels its content misrepresents or misses anything that needs to
> be discussed before it gets accepted/rejected, please let me know. As
> instructed in another thread, we should try and keep conversations here and
> not on github.
>
> Looking forward to the next steps!
>
> It looks good!

I thought so too, and earlier today had indicated such on the pull request
itself. -- Darren Duncan
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Thu, Oct 28, 2021 at 02:39:15PM -0300, breno wrote:
>
> ## Rejected Ideas
>
> I am unaware whether this feature has already been proposed for Perl 5
> in the past.
>

For completeness, I have (finally) looked in the archives for previous
proposals for the same feature. It used to be called "safe dereference"
or "safe arrow", and was discussed over a decade ago.

Here are the three threads I found:

Subject: [PATCH] Add "safe arrow/safe dereference" operator: &&->
From: David Caldwell
Date: Fri, 12 Nov 2010 20:56:43 -0800
Messages: 80, includes a patch
Link: https://www.nntp.perl.org/group/perl.perl5.porters/2010/11/msg165931.html

Subject: Semantics of safe-deref
From: Jesse Vincent
Date: Mon, 29 Nov 2010 16:26:57 -0500
Messages: 20
Link: https://www.nntp.perl.org/group/perl.perl5.porters/2010/11/msg166564.html

Subject: the old new safe dereference operator
From: Ricardo Signes
Date: Thu, 24 May 2012 21:51:16 -0400
Messages: 85
Link: https://www.nntp.perl.org/group/perl.perl5.porters/2012/05/msg187114.html

It would be worth reading them, in case some of things discussed then
were not discussed this time around. And maybe a ten years old patch can
still be somewhat useful.

--
Philippe Bruhat (BooK)

We laugh at stupidity because the alternative is to cry over it.
(Moral from Groo The Wanderer #71 (Epic))
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sun, Jul 17, 2022 at 4:24 PM Philippe Bruhat (BooK) <book@cpan.org>
wrote:

> For completeness, I have (finally) looked in the archives for previous
> proposals for the same feature. It used to be called "safe dereference"
> or "safe arrow", and was discussed over a decade ago.
>

Hi BooK!

Yes, when I turned this pre-RFC into a proper RFC draft I did my research
and eventually uncovered those past references too. I have used them not
only to update that (and other) parts of the draft but also to include in
the FAQ many of the past arguments and design decisions made to - hopefully
- overcome them and get the proposal accepted.

https://github.com/Perl/RFCs/pull/23
https://github.com/garu/RFCs/blob/garu/optional-chaining/rfcs/rfc0018.md#rejected-ideas

Speaking of which, what are the next steps?

Thanks!
garu
Re: Pre-RFC: Optional Chaining [ In reply to ]
On Sun, Jul 17, 2022 at 09:23:32PM +0200, Philippe Bruhat (BooK) wrote:
> On Thu, Oct 28, 2021 at 02:39:15PM -0300, breno wrote:
> >
> > ## Rejected Ideas
> >
> > I am unaware whether this feature has already been proposed for Perl 5
> > in the past.
> >
>
> For completeness, I have (finally) looked in the archives for previous
> proposals for the same feature. It used to be called "safe dereference"
> or "safe arrow", and was discussed over a decade ago.
>
> ...
>
> It would be worth reading them, in case some of things discussed then
> were not discussed this time around. And maybe a ten years old patch can
> still be somewhat useful.
>

After sending this, I felt obligated to read the 180+ emails in the
aforementioned threads.

Here are some highlights from the conversations:
* it started as &&-> and after much discussion and proposed alternatives about what the arrow
could look like, Jesse Vincent (then pumpking) settled on ?->
* a similar discussion about using a pragma happened, and reached
a similar conclusion
* the dereferencing overload operations were mentionned, and I _think_
the consensus was that there's no need to add new overload methods related
* what happens when the ?-> is part of an lvalue (and returns undef)?
* one example of autovivification in perldoc -f exists was mentionned
I expect it would "just work as expected" with ?->
* David Caldwell sent two sets of patches, I'm sure there's interesting
bits to look at in them, even if they don't apply anymore
* the ?-> (deref if defined) vs ->? (return undef if not can) came up,
this makes me thinks that once we have ?-> (topic of this RFC) and
->? (if someone writes the RFC for it), then surely we must have ?->?
* the case of the hash key pointing to undef was discussed
( $h->{foo}?->method() where the foo key does not exist vs the foo key
pointing to the value undef would behave the same)
* all the examples and counter examples in those threads would make
great test cases
* several people complained about the keyboard gymnastics needed to
type ?-> (but nobody compared that to the thing it replaces)
* being able to use the question mark (`?`) inside operators seem related
to the ?PAT? deprecation.

Names I've seen used for it:
* safe arrow
* defined dereference
* safe dereference
* maybe dereference
* safe-dereferencing arrow
* short-circuiting dereference
* conditional dereference
* defreference
* Twister™ (because of the difficult key combination needed to type it)
* undef-safe de-ref
* defined and dereference

My favorite quote from those threads, from Aristotle Pagaltzis, which I
think applies remarkably to today's post-facts trends:

> How can an opinion not founded in fact be changed by any change to
> the factual situation?

--
Philippe Bruhat (BooK)

Friendship is just brotherhood with a choice of brothers.
(Moral from Groo #9 (Image))
Re: Pre-RFC: Optional Chaining [ In reply to ]
Hello members of the PSC!

Following up on the RFC process, I believe all feedback regarding the
Optional Chaining proposal has been properly addressed and, since nothing
new came up after what I think was a good interval, I would like the PSC to
accept this draft as exploratory.

If anyone would like to take another look at the current revised version,
you can find it at:

https://github.com/garu/RFCs/blob/garu/optional-chaining/rfcs/rfc0018.md

and the PR itself, with extra discussion, is here:

https://github.com/Perl/RFCs/pull/23

Thank you very much for your time and consideration.

Cheers!
garu


On Tue, Jul 26, 2022 at 7:15 AM Philippe Bruhat (BooK) <book@cpan.org>
wrote:

> On Sun, Jul 17, 2022 at 09:23:32PM +0200, Philippe Bruhat (BooK) wrote:
> > On Thu, Oct 28, 2021 at 02:39:15PM -0300, breno wrote:
> > >
> > > ## Rejected Ideas
> > >
> > > I am unaware whether this feature has already been proposed for Perl 5
> > > in the past.
> > >
> >
> > For completeness, I have (finally) looked in the archives for previous
> > proposals for the same feature. It used to be called "safe dereference"
> > or "safe arrow", and was discussed over a decade ago.
> >
> > ...
> >
> > It would be worth reading them, in case some of things discussed then
> > were not discussed this time around. And maybe a ten years old patch can
> > still be somewhat useful.
> >
>
> After sending this, I felt obligated to read the 180+ emails in the
> aforementioned threads.
>
> Here are some highlights from the conversations:
> * it started as &&-> and after much discussion and proposed alternatives
> about what the arrow
> could look like, Jesse Vincent (then pumpking) settled on ?->
> * a similar discussion about using a pragma happened, and reached
> a similar conclusion
> * the dereferencing overload operations were mentionned, and I _think_
> the consensus was that there's no need to add new overload methods
> related
> * what happens when the ?-> is part of an lvalue (and returns undef)?
> * one example of autovivification in perldoc -f exists was mentionned
> I expect it would "just work as expected" with ?->
> * David Caldwell sent two sets of patches, I'm sure there's interesting
> bits to look at in them, even if they don't apply anymore
> * the ?-> (deref if defined) vs ->? (return undef if not can) came up,
> this makes me thinks that once we have ?-> (topic of this RFC) and
> ->? (if someone writes the RFC for it), then surely we must have ?->?
> * the case of the hash key pointing to undef was discussed
> ( $h->{foo}?->method() where the foo key does not exist vs the foo key
> pointing to the value undef would behave the same)
> * all the examples and counter examples in those threads would make
> great test cases
> * several people complained about the keyboard gymnastics needed to
> type ?-> (but nobody compared that to the thing it replaces)
> * being able to use the question mark (`?`) inside operators seem related
> to the ?PAT? deprecation.
>
> Names I've seen used for it:
> * safe arrow
> * defined dereference
> * safe dereference
> * maybe dereference
> * safe-dereferencing arrow
> * short-circuiting dereference
> * conditional dereference
> * defreference
> * Twister™ (because of the difficult key combination needed to type it)
> * undef-safe de-ref
> * defined and dereference
>
> My favorite quote from those threads, from Aristotle Pagaltzis, which I
> think applies remarkably to today's post-facts trends:
>
> > How can an opinion not founded in fact be changed by any change to
> > the factual situation?
>
> --
> Philippe Bruhat (BooK)
>
> Friendship is just brotherhood with a choice of brothers.
> (Moral from Groo #9
> (Image))
>