🔬67. Redeclaration of a symbol in Perl 6

🔬67. Redeclaration of a symbol in Raku

N. B. Perl 6 has been renamed to Raku. Click to read more.


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”

Leave a Reply

Your email address will not be published. Required fields are marked *

Retype the CAPTCHA code from the image
Change the CAPTCHA codeSpeak the CAPTCHA code