Why is Perl 6's loop variable declaration in the outer scope?

You can think of it as a block is what creates a lexical scope.

if (1) {
  my $a = 1;
}
$a;  # error
if (my $a = 1) {
}
$a;  # no error
loop (my $a = 1; $a == 1; ++$a) {
}
$a;  # no error

It's just that other C-like languages special case the (C style) for loop, and the Perl 6 design is to have as few special cases as possible.

So if something is very useful in one place, it should be designed so that it is useable everywhere.


For example -1 indexing in arrays.
Rather than just having @a[*-1] as special syntax constrained to array indexing, *-1 is just a way to create a lambda/closure that you can use anywhere you want a simple lambda.

Another example is pointy blocks being more than just a way to declare the iteration variable(s) in a for loop.

for @a -> $value {…}

They are usable with any similar construct.

if $a.some-resource-expensive-op -> $result {…}

Instead of

{
  # note that $result isn't read-only like it is above
  my $result = $a.some-resource-expensive-op;
  if $result {…}
}

You can even use it as just another syntax to create a lambda.

@a.sort: -> $value {…}

In order to make the language simpler to reason about, special cases need to pull their own weight, and having only the arguments of the loop construct, and no other construct, be part of the block just doesn't.
If it were generally useful then it would be different.

Also the loop construct is one of the few features that doesn't really fit with the general design aesthetic of Perl 6. I think it might be easier to argue for its deprecation and removal than for it to be more special cased.
(no one is arguing for it to be removed)


I see the use case in something like this:

loop ( my $n = 0; $n < 3; $n++ ) {
    put $n;
    last if 2.rand.Int;  # some run-time condition
}
say $n == 3 ?? 'Completed' !! 'Aborted';

This would be hard to do with:

for ^3 -> $n {
}

which is the for equivalent.

Other than that, I have no idea.