[% setvar title New pragma 'scope' to change Perl's default scoping %]
Note: these documents may be out of date. Do not use as reference! |
To see what is currently happening visit http://www.perl6.org/
New pragma 'scope' to change Perl's default scoping
Maintainer: Nathan Wiger <nate@wiger.org> Date: 7 Aug 2000 Last Modified: 28 Aug 2000 Mailing List: perl6-language-strict@perl.org Number: 64 Version: 2 Status: Frozen
This RFC was relatively well-received. While many questioned how useful
use scope 'blocks'
could be, overall most people were quite receptive
to the use scope 'subs'
concept.
This RFC has been frozen. It is not expected to grow further. Since it is a pragma it is not a core piece of functionality and could be added at any time.
Historically, Perl has had the default "everything's global" scope. This means that you must explicitly define every variable with my, our, or its absolute package to get better scoping. You can 'use strict' to force this behavior, but this makes easy things harder, and doesn't fix the actual problem.
Those who don't learn from history are doomed to repeat it. Let's fix this.
Tons of Impatience, a good amount of Laziness, and a dash of Hubris
First off, let's clear up what this RFC does NOT do: it does NOT change Perl's default scoping. By default, Perl should be scoped globally, as it always has been and always should continue to be. Rather, this RFC suggests a pragma that will offer alternative scoping rules, to make easy things even easier.
In a default Perl script, everything is global. Which means in:
$x = 10; if (1) { unless (undef) { $y = 1 if ($x); } }
$x and $y are in the same scope. This is good, but also bad. For one thing, you can hang yourself real easily if you come from a C background and expect this to keep stuff private:
$x = 10; sub square { ($x) = @_; $x *= $x; } $ans = square($x); print "$x squared is $ans\n"; # "100 squared is 100" ?
Ooops. What happened? Turns out the sub square()'s $x is the same $x as the one outside. Bad news, you reset the (global) value of $x. Ooops.
RFC 6 addresses this problem, but in the wrong way. In Perl, you can 'use strict' to force you to predeclare all your variables with my or our (which you can do anyways if you feel like it). So the above code becomes:
use strict; my $x = 10; sub square { my($x) = @_; $x *= $x; } my $ans = square($x); print "$x squared is $ans\n";
So, with the addition of use strict
and 3 "little" my
's, we've
resolved the ambiguity. Problem is, this is a good amount of work
(especially if you're Lazy *and* Impatient, like I am) for such a stupid
little 5-line script. Plus, it makes the code markedly harder to read.
Setting 'use strict' to the default makes easy things harder - very
UN-Perlish! Eck.
The better solution is to fix the actual *problem* (Perl's scoping), and provide a method for automatically scoping variables via different algorithms than Perl's default. As Daniel Chetlin notes in RFC 16 (against strict defaults):
Making strict 'vars' default appears to be an attempt to fix a hole in the floor by permanently barring access to the room in which the hole is, rather than just patching the hole.
Let's patch the hole.
This RFC describes several different types of scoping. They can be used
together, yielding advanced scoping without the mess of my
. Each of
these would be specified as:
use scope qw(types);
Where "types" are the types listed below. By default, 'all' is used,
which turns on all scoping rules. As of version 1, there are currently
two proposed types of scopes: subs
and blocks
.
Note: You can always still use an explicit my
or our
keyword to
override the scope
pragma. The scope
pragma only affects variables
that are not explicitly declared. This is important, since it means that
no functionality is reduced by specifying use scope
.
The subs
scoping pragma makes one key change: it automatically scopes
variables within subs as if they were my
'ed at the top of the sub.
So, the code:
use scope 'subs'; sub dostuff { ($one, $two, $three) = @_ if ( $two > 5 ) { $four = 1; } else { $five = 2; while ( $five < 6 ) { $five++; } } return $four || $five; }
Would be internally changed to the same thing as if all of dostuff
's
variables had been declared with my
as the first statement in the
sub. This keeps you from clobbering yourself with:
($three, $four, $five) = readvals; $six = dostuff($five, $three, $four);
Without the need to declare all those my
's all over the place.
The exceptions to the subs
scoping pragma are BEGIN, END, and any
other special Perl subs that may apply.
The blocks
scoping pragma automatically scopes variables to the
innermost anonymous block. The key work here is anonymous. The main
program itself is seen as the outermost block. So, this code:
$x = 10; { $x = 5; } if ($x == 10) { $x = 25; } print "$x\n";
Would result in "25" being printed out. Here's why:
1. The C<$x = 10> is automatically scoped with its own C<my>. 2. The C<$x = 5> inside the B<anonymous> block is automatically scoped with its own C<my>. 3. The C<$x = 25> code, however, inside the C<if> statement is not scoped, since it is not an B<anonymous> block. Since there is already an C<$x> in the current block, there is no need to rescope it.
Th blocks
scoping pragma also works for do{} blocks, but again not
for BEGIN or END (but those are subs anyways).
To illustrate, here are some examples of how this might be used:
1. do {} block $val = do { $x = 10; # ... stuff happens ... $y; }; In which case $val = $y. 2. explicit our() scoping $x = 10; our $y = 10; { $x = 5; # auto-my'ed $y += $x; # $y already our'ed above } print "$y"; # 15
Admittedly, the usefulness of this is markedly less than the use scope
'subs'
pragma.
By its very nature, this is incompatible with strict
. Specifying both
use strict
and <use scope> should generate an error.
Hopefully simple. If you preparsed 'subs' or 'blocks' and just pseudo- inserted a "my" in front of every variable, you'd be 99% of the way there.
This introduces new functionality. No migration path is required.
RFC 16: Keep default Perl free of constraints such as warnings and strict.
RFC 28: Perl should stay Perl.