develooper Front page | perl.perl5.porters | Postings from June 2021

Re: RFC: Multiple-alias syntax for for

Thread Previous | Thread Next
Nicholas Clark
June 14, 2021 09:22
Re: RFC: Multiple-alias syntax for for
Message ID:
On Thu, Jun 10, 2021 at 04:24:42AM -0400, Dan Book wrote:
> On Thu, Jun 10, 2021 at 3:28 AM Nicholas Clark <> wrote:
> >
> > I admit some guilt and inconsistency here - *I* preferred using `for` in
> > the code examples because it's 4 characters shorter.
> >
> > *but* I realised that that makes the title `Multiple-alias syntax for for`
> > which is daft. So I changed *that* to `foreach`. And now it's inconsistent.
> > Bad programmer, no cookie.
> >
> > I don't know what is best.
> >
> My preference is to refer to the syntax construct descriptively as
> "foreach", since although it is an exact synonym in the grammar, the
> C-style for loop is never referred to as a "foreach". Separately from what
> any person may decide to write in the actual code.

It turns out that this is how the docs do it. (More below)

> > I think the slightly better question would be express it as
> >
> >     for my ($a, undef, $c) (1 .. 9) { ... }
> >
> >
> > It's a reasonable generalisation of list assignment, where you can assign
> > to
> > undef to mean "ignore this value". I can see that this *might* be useful.
> > It's also safe syntax to implement (although I didn't try, and I'm not sure
> > how hard it would be).
> >
> > I'd like to stick to forbidding this, because it makes the implementation
> > harder.
> >
> I also see how it could be useful, but on the other hand it is exactly the
> same in practice as my ($x, $y, $z) but with some (imagined or real)
> microoptimization of not aliasing the second value/using up that variable
> name. I don't think it's useful enough to outweigh complication to the
> implementation.

I think this too, but I realise that one of us might spot a good way to do
it that doesn't have much extra complication. In particular, I suspect that
if it's possible to set the Pad up the same way that anonymous entries such
as TARG slots are represented, then there might be zero runtime overhead
for "aliasing" to "undef".

So, anyway, I think that we've got to this stage:

                             Provisional RFC
              "we think this idea is worth implementing"
              (We have a firm idea of what we want to do)

There are 4 open issues that I can see, that I'm not sure about:

1) Should this be behind a feature guard?
2) Should it issue an experimental warning?
3) Are there any examples that aren't already in the *Motivation* section?
4) Are there any *Security Implications*?

Right now my test implementation doesn't guard or warn:

but I think if either are needed, the "right" place is in `yyl_foreach`

I filled out the "Specification" and "Backwards Compatibility" sections.

I like the idea of the "Specification" just *being* the proposed end-user
documentation, but I'm not sure if a diff-hunk is the best way to express
this. I used one because it seemed the easiest way to say "I need to change
the Pod file in these three places, in this way". So diff and context :-)

Nicholas Clark


## Specification

diff --git a/pod/perlsyn.pod b/pod/perlsyn.pod
index fe511f052e..490119d00e 100644
--- a/pod/perlsyn.pod
+++ b/pod/perlsyn.pod
@@ -282,6 +282,14 @@ The following compound statements may be used to control flow:


+As of Perl 5.36, you can iterate over multiple values at a time by specifying
+a list of lexicals within parentheses
+    LABEL for my (VAR, VAR) (LIST) BLOCK
+    LABEL for my (VAR, VAR) (LIST) BLOCK continue BLOCK
+    LABEL foreach my (VAR, VAR) (LIST) BLOCK
+    LABEL foreach my (VAR, VAR) (LIST) BLOCK continue BLOCK
 If enabled by the experimental C<try> feature, the following may also be used

     try BLOCK catch (VAR) BLOCK
@@ -549,6 +557,14 @@ followed by C<my>.  To use this form, you must enable the C<refaliasing>
 feature via C<use feature>.  (See L<feature>.  See also L<perlref/Assigning
 to References>.)

+As of Perl 5.36, you can iterate over a list of lexical scalars n-at-a-time.
+If the size of the LIST is not an exact multiple of number of iterator
+variables, then on the last iteration the "excess" iterator variables are
+undefined values, much like if you slice beyond the end of an array.  You
+can only iterate over scalars - unlike list assignment, it's not possible to
+use C<undef> to signify a value that isn't wanted.  This is a limitation of
+the current implementation, and might be changed in the future.

     for (@ary) { s/foo/bar/ }
@@ -574,6 +590,17 @@ Examples:
         # do something which each %hash

+    foreach my ($foo, $bar, $baz) (@list) {
+        # do something three-at-a-time
+    }
+    foreach my ($key, $value) (%hash) {
+        # iterate over the hash
+        # The hash is eagerly flattened to a list before the loop starts,
+        # but as ever keys are copies, values are aliases.
+        # This is the same behaviour as for $var (%hash) {...}
+    }
 Here's how a C programmer might code up a particular algorithm in Perl:

     for (my $i = 0; $i < @ary1; $i++) {

## Backwards Compatibility

The new syntax is a syntax error on existing Perls. It generates this error message

`Missing $ on loop variable at /home/nick/test/ line 4.`

Existing static tooling should be able to recognise it as a syntax error, but without changes wouldn't be able to give any better diagnostics.

The proposed implementation has tests and patches for `B::Deparse` and `B::Concise`. `Devel::Cover`, `Devel::NYTProf` and `Perl::Critic` handle the new syntax just fine.

I don't think that we have any way to emulate this syntax for earlier Perls. I don't have any feel for what sort of API we would need to add to the parser to make it possible in the future, and whether any API we could add would be fragile and tightly coupled to the exact current implementation.


Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About