Apocalypse 6
by Larry Wall
|
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
Editor's Note: this Apocalypse is out of date and remains here for historic reasons. See Synopsis 06 for the latest information.
The slurpy hash
A hash declaration like *%named indicates that the %named
hash should slurp up all the remaining named arguments (that is,
those that aren't bound explicitly to a specific formal parameter).
The slurpy array
An array declaration like *@rest indicates that the @rest array
should slurp up all the remaining items after the named parameters.
(Later we'll discuss how to disambiguate the situation when the
beginning of your list looks like named parameters.) If you shift
or pop without an argument, it shifts or pops whatever slurpy array
is in scope. (So in a sense, your main program has an implicit slurpy
array of *@*ARGS because that's what shift shifts there.)
Formal parameter syntax
Formal parameters have lexical scope, as if they were declared with
a my. (That is reflected in the pseudocode in Appendix B.)
Their scope extends only to the end of the associated block.
Formal parameters are the only lexically scoped variables that are
allowed to be declared outside their blocks. (Ordinary my and
our declarations are always scoped to their surrounding block.)
Any subroutine can have a method signature syntactically, but
subsequent semantic analysis will reject mistakes like invocants on
subroutines. This is not just motivated by laziness. I think that
"You can't have an invocant on a subroutine" is a better error
message than "Syntax error".
rule signature :w {
[<parameter> [<[,:]> <parameter> ]* ]?
}
In fact, we just treat colon as a funny comma here, so any use of
extra colons is detected in semantic analysis. Similarly, zone
markers are semantically restricted, not syntactically. Again,
"Syntax error" doesn't tell you much. It's much more informative
to see "You can't declare an optional positional parameter like
?$flag after a slurpy parameter like *@list", or "You can't use
a zone marker on an invocant".
Here's what an individual parameter looks like:
rule parameter :w {
[ <type>? <zone>? <variable> <trait>* <defval>?
| \[ <signature> \] # treat single array ref as an arg list
]
}
rule zone {
[ \? # optional positional
| \* # slurpy array or hash
| \+ # optional named-only
]
}
rule variable { <sigil> <name> [ \( <siglet> \) ]? }
rule sigil { <[$@%&]> <[*.?^]>? } # "What is that, swearing?"
Likewise, we parse any sigil here, but semantically reject things like $*x
or $?x. We also reject package-qualified names and indirect names.
We could have a <simplevar> rule that only admits <ident>,
but again, "Syntax error" is a lot less user-friendly than "You can't
use a package variable as a parameter, dimwit!"
Similarly, the optional <siglet> in <variable> is allowed
only on & parameters, to say what you expect the signature of
the referenced subroutine to look like. We should talk about siglets.
Siglets
The <siglet> in the <variable> rule is an example of
a nameless signature, that is, a "small signature", or "siglet".
Signatures without names are also used for return types and context
traits (explained later). A siglet is sequential list of paramlets.
The paramlets do not refer to actual variable names, nor do they
take defaults:
rule siglet :w {
[<paramlet> [<[,:]> <paramlet> ]* ]?
}
rule paramlet :w {
[ <type> <zone>? <varlet>? <trait>* # require type
| <zone> <varlet>? <trait>* # or zone
| <varlet> <trait>* # or varlet
| \[ <siglet> \] # treat single array ref as an arg list
]
}
In place of a <variable>, there's a kind of stub we'll call a "varlet":
rule varlet :w {
<sigil> [ \( <siglet \) ]?
}
As with the <variable> rule, a <varlet>'s optional
siglet is allowed only on & parameters.
Here's a fancy example with one signature and several siglets.
sub (int *@) imap ((int *@) &block(int $),
int *@vector is context(int) {...}
You're not expected to understand all of that yet. What you should
notice, however, is that a paramlet is allowed to be reduced to
a type (such as int), or a zone (such as ?), or a varlet (such as $), or some sequence
of those (such as int *@). But it's not allowed to be reduced to a null
string. A signature of () indicates zero arguments, not one argument
that could be anything. Use ($) for that. Nor can you specify four
arguments by saying (,,,). You have to put something there.
Perl 6 siglets can boil down to something very much like Perl 5's
"prototype pills". However, you can't leave out the comma between
parameters in Perl 6. So you have to say ($,$) rather than ($$),
when you want to indicate a list of two scalars.
If you use a <siglet> instead of a <signature>
in declaring a subroutine, it will be taken as a Perl 5 style
prototype, and all args still come in via @_. This is a sop to
the Perl5-to-Perl6 translator, which may not be able to figure out
how to translate a prototype to a signature if you've done something
strange with @_. You should not use this feature in new code.
If you use a siglet on a stub declaration, you must use the same siglet
on the corresponding definition as well, and vice versa. You can't
mix siglets and signatures that way. (This is not a special rule,
but a natural consequence of the signature matching rules.)
Siglets and multimethods
For closure parameters like &block(int $), the associated siglet
is considered part of its name. This is true not just for parameters,
but anywhere you use the & form in your program, because with
multimethods there may be several routines sharing the same identifier,
distinguishable only by their type signature:
multi factorial(int $a) { $a<=1 ?? 1 :: $a*factorial($a-1) }
multi factorial(num $a) { gamma(1+$a) }
$ref = &factorial; # illegal--too ambiguous
$ref = &factorial($); # illegal--too ambiguous
$ref = &factorial(int); # good, means first one.
$ref = &factorial(num); # good, means second one.
$ref = &factorial(complex); # bad, no such multimethod.
Note that when following a name like "&factorial", parentheses do not
automatically mean to make a call to the subroutine. (This Apocalypse
contradicts earlier Apocalypses. Guess which one is right...)
$val = &factorial($x); # illegal, must use either
$val = factorial($x); # this or
$val = &factorial.($x); # maybe this.
In general, don't use the & form when you really want to call
something.
Formal parameter traits
Other than type, zone, and variable name, all other information about
parameters is specified by the standard trait syntax, generally
introduced by is. Internally even the type and zone are just
traits, but syntactically they're out in front for psychological
reasons. Whose psychological reasons we won't discuss.
is constant (default)
Every formal parameter is constant by default, meaning primarily that the compiler won't feel obligated to construct an lvalue out the actual argument unless you specifically tell it to. It also means that you may not modify the parameter variable in any way. If the parameter is a reference, you may use it to modify the referenced object (if the object lets you), but you can't assign to it and change the original variable passed to the routine.
is rw
The rw trait is how you tell the compiler to ask for an lvalue
when evaluating the actual argument for this parameter. Do not
confuse this with the rw trait on the subroutine as a whole, which
says that the entire subroutine knows how to function as an lvalue.
If you set this trait, then you may modify the variable that was
passed as the actual argument. A swap routine would be:
sub swap ($a is rw, $b is rw) { ($a,$b) = ($b,$a) }
If applied to a slurpy parameter, the rw trait distributes to each
element of the list that is bound to the parameter. In the case of
a slurpy hash, this implies that the named pairs are in an lvalue
context, which actually puts the right side of each named pair into
lvalue context.
Since normal lvalues assume "is rw", I suppose that also implies
that you can assign to a pair:
(key => $var) = "value";
or even do named parameter binding:
(who => $name, why => $reason) := (why => $because, who => "me");
which is the same as:
$name := "me";
$reason := $because;
And since a slurpy hash soaks up the rest of the named parameters,
this also seems to imply that binding a slurpy rw hash actually
makes the hash values into rw aliases:
$a = "a"; $b = "b";
*%hash := (a => $a, b => $b);
%hash{a} = 'x';
print $a; # prints "x"
That's kinda scary powerful. I'm not sure I want to document that... ["Too late!" whispers Evil Damian.]
is copy
This trait requests copy-in semantics. The variable is modifiable
by you, but you're only modifying your own private copy. It has the
same effects as assigning the argument to your own my variable.
It does not do copy-out.
If you want both copy-in and copy-out semantics, declare it rw
and do your own copying back and forth, preferably with something
that works even if you exit by exception (if that's what you want):
sub cico ($x is rw) {
my $copy = $x;
LAST { $x = $copy }
...
}
Though if you're using a copy you probably only want to copy-out on
success, so you'd use a KEEP block instead. Or more succinctly,
using the new will syntax:
sub cicomaybe ($x is rw) {
my $copy will keep { $x = $copy } = $x;
...
}
is ref
This trait explicitly requests call-by-reference semantics. It lets
you read and write an existing argument but doesn't attempt to coerce
that argument to an lvalue (or autovivify it) on the caller end,
as rw would. This trait is distinguished from a parameter of type
Ref, which merely asserts that the return type of the parameter is a
reference without necessarily saying anything about calling convention.
You can without contradiction say:
sub copyref (Ref $ref is copy) {...}
meaning you can modify $ref, but that doesn't change whatever was
passed as the argument for that parameter.
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 |

