Categories
Posts in this category
- Introduction
- Strings, Arrays, Hashes;
- Types
- Basic Control Structures
- Subroutines and Signatures
- Objects and Classes
- Contexts
- Regexes (also called "rules")
- Junctions
- Comparing and Matching
- Containers and Values
- Where we are now - an update
- Changes to Perl 5 Operators
- Laziness
- Custom Operators
- The MAIN sub
- Twigils
- Enums
- Unicode
- Scoping
- Regexes strike back
- A grammar for (pseudo) XML
- Subset Types
- The State of the implementations
- Quoting and Parsing
- The Reduction Meta Operator
- The Cross Meta Operator
- Exceptions and control exceptions
- Common Perl 6 data processing idioms
- Currying
Sat, 29 Nov 2008
Scoping
Permanent link
NAME
"Perl 5 to 6" Lesson 18 - Scoping
SYNOPSIS
for 1 .. 10 -> $a { # $a visible here } # $a not visible here while my $b = get_stuff() { # $b visible here } # $b still visible here my $c = 5; { my $c = $c; # $c is undef here } # $c is 5 here my $y; my $x = $y + 2 while $y = calc(); # $x still visible
DESCRIPTION
Lexical Scoping
Scoping in Perl 6 is quite similar to that of Perl 5. A Block introduces a new lexical scope. A variable name is searched in the innermost lexical scope first, if it's not found it is then searched for in the next outer scope and so on. Just like in Perl 5 a my
variable is a proper lexical variable, and an our
declaration introduces a lexical alias for a package variable.
But there are subtle differences: variables are exactly visible in the rest of the block where they are declared, variables declared in block headers (for example in the condition of a while
loop) are not limited to the block afterwards.
Also Perl 6 only ever looks up unqualified names (variables and subroutines) in lexical scopes.
If you want to limit the scope, you can use formal parameters to the block:
if calc() -> $result { # you can use $result here } # $result not visible here
Variables are visible immediately after they are declared, not at the end of the statement as in Perl 5.
my $x = .... ; ^^^^^ $x visible here in Perl 6 but not in Perl 5
Dynamic scoping
The local
adjective is now called temp
, and if it's not followed by an initialization the previous value of that variable is used (not undef
).
There's also a new kind of dynamically scoped variable called a hypothetical variable. If the block is left with an exception or a false value,, then the previous value of the variable is restored. If not, it is kept:
use v6; my $x = 0; sub tryit($success) { let $x = 42; die "Not like this!" unless $success; return True; } tryit True; say $x; # 42 $x = 0; try tryit False; say $x; # 0
Context variables
Some variables that are global in Perl 5 ($!
, $_
) are context variables in Perl 6, that is they are passed between dynamic scopes.
This solves an old Problem in Perl 5. In Perl 5 an DESTROY
sub can be called at a block exit, and accidentally change the value of a global variable, for example one of the error variables:
# Broken Perl 5 code here: sub DESTROY { eval { 1 }; } eval { my $x = bless {}; die "Death\n"; }; print $@ if $@; # No output here
In Perl 6 this problem is avoided by not implicitly using global variables.
(In Perl 5.14 there is a workaround that protects $@
from being modified, thus averting the most harm from this particular example.)
Pseudo-packages
If a variable is hidden by another lexical variable of the same name, it can be accessed with the OUTER
pseudo package
my $x = 3; { my $x = 10; say $x; # 10 say $OUTER::x; # 3 say OUTER::<$x> # 3 }
Likewise a function can access variables from its caller with the CALLER
and CONTEXT
pseudo packages. The difference is that CALLER
only accesses the scope of the immediate caller, CONTEXT
works like UNIX environment variables (and should only be used internally by the compiler for handling $_
, $!
and the like). To access variables from the outer dynamic scope they must be declared with is context
.
MOTIVATION
It is now common knowledge that global variables are really bad, and cause lots of problems. We also have the resources to implement better scoping mechanism. Therefore global variables are only used for inherently global data (like %*ENV
or $*PID
).
The block scoping rules haven been greatly simplified.
Here's a quote from Perl 5's perlsyn
document; we don't want similar things in Perl 6:
NOTE: The behaviour of a "my" statement modified with a statement modifier conditional or loop construct (e.g. "my $x if ...") is undefined. The value of the "my" variable may be "undef", any previously assigned value, or possibly anything else. Don't rely on it. Future versions of perl might do something different from the version of perl you try it out on. Here be dragons.
SEE ALSO
S04 discusses block scoping: http://design.perl6.org/S04.html.
S02 lists all pseudo packages and explains context scoping: http://design.perl6.org/S02.html#Names.