Exegesis 3
by Damian Conway
|
Pages: 1, 2, 3, 4, 5, 6, 7
Editor's note: this document is out of date and remains here for historic interest. See Synopsis 3 for the current design information.
A parameter by any other name
Now that the I/O is organized, we can get down to the actual processing. First, we load the data:my %data = load_data(filename=>'weblog', version=>1);Note that we're using named arguments here. This attempt would blow up badly in Perl 5, because we didn't set &load_data up to expect a hash-like list of arguments. But it works fine in Perl 6 for two reasons:
- Because we did set up &load_data with named parameters; and
- Because the => operator isn't in Kansas anymore.
In Perl 6, => is a fully-fledged anonymous object constructor -- like [...] and {...}. The objects it constructs are called "pairs" and they consist of a key (the left operand of the =>), and a value (the right operand). The key is still stringified if it's a valid identifier, but both the key and the value can be any kind of Perl data structure. They are accessed via the pair object's key and value methods:
my $pair_ref = [1..9] => "digit";
print $pair_ref.value; # prints "digit"
print $pair_ref.key.[3]; # prints 4
So, rather than getting four arguments:
load_data('filename', 'weblog', 'version', 1); # Perl 5 semantics
&load_data gets just two arguments, each of which is a reference
to a pair:
load_data( $pair_ref1, $pair_ref2); # Perl 6 semanticsWhen the subroutine dispatch mechanism detects one or more pairs as arguments to a subroutine with named parameters, it examines the keys of the pairs and binds their values to the correspondingly named parameters -- no matter what order the paired arguments originally appeared in. Any remaining non-pair arguments are then bound to the remaining parameters in left-to-right order.
So we could call &load_data in any of the following ways:
load_data(filename=>'weblog', version=>1); # named
load_data(version=>1, filename=>'weblog'); # named (order doesn't matter)
load_data('weblog', 1); # positional (order matters)
There are numerous other uses for pairs, one of which we'll see shortly.
Please queue for processing
Having loaded the data, we go into a loop and iterate over each file's information. First, we announce the file and its internal name: foreach my $file (keys %data) {
print "$file contains data on %data{$file}{name}\n";
The Xor-twist
Then we toggle the "is active" status bit (the eighth bit) for each file. To flip that single bit without changing any of the other status bits, we bitwise-xor the status bitset against the bitstring 0000000010000000. Each bit xor'd against a zero stays as it is (0 xor 0 --> 0; 1 xor 0 --> 1), while xor'ing the eighth bit against 1 complements it (0 xor 1 --> 1; 1 xor 1 --> 0).But because the caret has been appropriated as the Perl 6 hyper-operator prefix, it will no longer be used as bitwise xor. Instead, binary tilde will be used:
%data{$file}{stat} = %data{$file}{stat} ~ $is_active_bit;
This is actually an improvement in syntactic consistency since bitwise xor (now binary ~)
and bitwise complement (still unary ~) are mathematically related:
~x
is (-1~x).
Note that we could have used the assignment variant of binary ~:
%data{$file}{stat} ~= $is_active_bit; # flip only bit 8 of status bitset
but that's probably best avoided due to its confusability with the much
commoner "pattern association" operator:
%data{$file}{stat} =~ $is_active_bit; # match if status bitset is "128"
By the way, there is also a high precedence logical xor operator in
Perl 6. You guessed it: ~~. This finally fills the strange
gap in Perl's logical operator set:
Binary (low) | Binary (high) | Bitwise
______________|_______________|_____________
| |
or | || | |
| |
and | && | &
| |
xor | ~~ | ~
| |
And it will also help to reduce programmer stress by allowing us to write:
$make = $money ~~ $fast;instead of (the clearly over-excited):
$make = !$money != !$fast;
Bound for glory
In both Perl 5 and 6, it's possible to create an alias for a variable. For example, the subroutine: sub increment { $_[0]++ } # Perl 5
sub increment { @_[0]++ } # Perl 6
works because the elements of @_ become aliases for whatever variable
is passed as their corresponding argument. Similarly, one can use a
for
to implement a Pascal-ish with:
for my $age ( $person[$n]{data}{personal}{time_dependent}{age} ) {
if ($age < 12) { print "Child" }
elsif ($age < 18) { print "Adolescent" }
elsif ($age < 25) { print "Junior citizen" }
elsif ($age < 65) { print "Citizen" }
else { print "Senior citizen" }
}
Perl 6 provides a more direct mechanism for aliasing one variable
to another in this way: the := (or "binding") operator. For
example, we could rewrite the previous example like so in Perl 6:
my $age := $person[$n]{data}{personal}{time_dependent}{age};
if ($age < 12) { print "Child" }
elsif ($age < 18) { print "Adolescent" }
elsif ($age < 25) { print "Junior citizen" }
elsif ($age < 65) { print "Citizen" }
else { print "Senior citizen" }
Bound aliases are particularly useful for temporarily giving a conveniently short
identifier to a variable with a long or complex name. Scalars,
arrays, hashes and even subroutines may all be given less sequipedalian
names in this way:
my @list := @They::never::would::be::missed::No_never_would_be_missed;
our %plan := %{$planning.[$planner].{planned}.[$planet]};
temp &rule := &FulfilMyGrandMegalomanicalDestinyBwahHaHaHaaaa;
In our example program, we use aliasing to avoid having to write @%data{$file}{costs}
everywhere:
my @costs := @%data{$file}{costs};
An important feature of the binding operator is that the lvalue (or lvalues)
on the left side form a context specification for the rvalue (or rvalues)
on the right side. It's as if the lvalues were the parameters of an invisible
subroutine, and the rvalues were the corresponding arguments being passed
to it. So, for example, we could also have written:
my @costs := %data{$file}{costs};
(i.e. without the @ dereferencer) because the lvalue expects
an array as the corresponding rvalue, so Perl 6 automatically dereferences
the array reference in %data{$file}{costs} to provide that.
More interestingly, if we have both lvalue and rvalue lists, then each of the rvalues is evaluated in the context specified by its corresponding lvalue. For example:
(@x, @y) := (@a, @b);aliases @x to @a, and @y to @b, because @'s on the left act like @ parameters, which require -- and bind to -- an unflattened array as their corresponding argument. Likewise:
($x, %y, @z) := (1, {b=>2}, %c{list});
binds $x to the value 1 (i.e. $x becomes a constant),
%y
to the anonymous hash constructed by {b=>2}, and @z to
the array referred to by %c{list}. In other words,
it's the same set of bindings we'd see if we wrote:
sub foo($x, %y, @z) {...}
foo(1, {b=>2}, %c{list});
except that the := binding takes effect in the current scope.
And because := works that way, we can also use the flattening operator (unary *) on either side of such bindings. For example:
(@x, *@y) := (@a, $b, @c, %d);aliases @x to @a, and causes @y to bind to the remainder of the lvalues -- by flattening out $b, @c, and %d into a list and then slurping up all their components together.
Note that @y is still an alias for those various slurped components. So @y[0] is an alias for $b, @y[1..@c.length] are aliases for the elements of @c, and the remaining elements of @y are aliases for the interlaced keys and values of %d.
When the star is on the other side of the binding, as in:
($x, $y) := (*@a);then @a is flattened before it is bound, so $x becomes an alias for @a[0] and $y becomes an alias for @a[1].
The binding operator will have many uses in Perl 6 (most of which we probably haven't even thought of yet), but one of the commonest will almost certainly be as an easy way to swap two arrays efficiently:
(@x, @y) := (@y, @x);Yet another way to think about the binding operator is to consider it as a sanitized version of those dreaded Perl 5 typeglob assignments. That is:
$age := $person[$n]{data}{personal}{time_dependent}{age};
is the same as Perl 5's:
*age = \$person->[$n]{data}{personal}{time_dependent}{age};
except that it also works if $age is declared as a lexical.
Oh, and binding is much safer than typeglobbing was, because it explicitly requires that $person[$n]{data}{personal}{time_dependent}{age} evaluate to a scalar, whereas the Perl 5 typeglob version would happily (and silently!) replace @age, %age, or even &age if the rvalue happened to produce a reference to an array, hash, or subroutine instead of a scalar.

