Today, we will see how Perl 6 helps to keep our programs better.
Redeclaration of a variable
Examine the following program:
my $x = 1; my $x = 2; say $x;
You can immediately see that this program is not entirely correct. Either we meant to assign a new value to $x or to create a new variable with a different name. In either case, compiler has no idea and complains:
$ perl6 redecl.pl
Potential difficulties:
Redeclaration of symbol '$x'
at /Users/ash/redecl.pl:2
------> my $x⏏ = 2;
2
You see a runtime warning, while the program does not stop. Let us find out where it happens in the source code.
When you declare a variable, the grammar matches the corresponding text and calls the variable_declarator action method. It is quite compact but nevertheless I will not quote it completely.
class Perl6::Actions is HLL::Actions does STDActions { . . . method variable_declarator($/) { . . . } . . . }
By the way, you can see here how Perl 6 treats a variable name:
my $past := $<variable>.ast;
my $sigil := $<variable><sigil>;
my $twigil := $<variable><twigil>;
my $desigilname := ~$<variable><desigilname>;
my $name := $sigil ~ $twigil ~ $desigilname;
The name of a variable is a concatenation of a sigil, a twigil and an identifier (which is called desigiled name in the code).
Then, if we’ve got a proper variable name, check it against an existing lexpad:
if $<variable><desigilname> { my $lex := $*W.cur_lexpad(); if $lex.symbol($name) { $/.typed_worry('X::Redeclaration', symbol => $name); }
If the name is known, generate a warning. If everything is fine, create a variable declaration:
make declare_variable($/, $past, ~$sigil, ~$twigil, $desigilname, $<trait>, $<semilist>, :@post);
Redeclaration of a routine
Now, let us try to re-create a subroutine:
sub f() {} sub f() {}
This may only be OK if the subs are declared as multi-subs. With the given code, the program will not even compile:
===SORRY!=== Error while compiling /Users/ash/redecl.pl
Redeclaration of routine 'f' (did you mean to declare a multi-sub?)
at /Users/ash/redecl.pl:6
------> sub f() {}⏏<EOL>
This time, it happens in a much more complicated method, routine_def:
method routine_def($/) { . . . my $predeclared := $outer.symbol($name); if $predeclared { my $Routine := $*W.find_symbol(['Routine'], :setting-only); unless nqp::istype( $predeclared<value>, $Routine) && nqp::getattr_i($predeclared<value>, $Routine, '$!yada') { $*W.throw($/, ['X', 'Redeclaration'], symbol => ~$<deflongname>.ast, what => 'routine', ); } }
The exception
The code of the exception is rather simple. Here it is:
my class X::Redeclaration does X::Comp { has $.symbol; has $.postfix = ''; has $.what = 'symbol'; method message() { "Redeclaration of $.what '$.symbol'" ~ (" $.postfix" if $.postfix) ~ (" (did you mean to declare a multi-sub?)" if $.what eq 'routine'); } }
As you see, depending on the value of $.what, it prints either a short message or adds a suggestion to use the multi keyword.
2 thoughts on “🔬67. Redeclaration of a symbol in Perl 6”