Search code examples
namespacesprototypeperltypeglob

Perl: prototype in anonymous subroutine


I am currently learning about Perls system of typeglobs and namespaces. So I wrote a module that takes two arguments the value and the name of a constant and exports the constant to the caller. The $package variable is equal to caller[2].

*{"$package::$name"} = sub () { return $value; };

This code above does the job of exporting a anonymous subroutine into the callers symboltable. Because my goal is to build my own constant-implementation the subroutine has a empty prototype which means its a read-only subroutine.

But this is my problem: the prototype does not work. So

print &TestConst; #works well
print TestConst(); #works well
print TestConst; #Name "main::TestConst" used only once: possible typo at testscript.pl line 7.

Is there something wrong in my thoughts? Is there another way of doing it?


Solution

  • You can define all the symbols you want during runtime, but prototypes will only affect code compiled afterwards since prototypes affect how calls to the sub are parsed and compiled. For example:

    use strict;
    use warnings;
    
    package Foo;
    
    BEGIN {
        *Foo::bar = sub () { 42 };
    }
    
    *Foo::baz = sub () { 43 };
    
    my $bar = bar;
    my $baz = baz;
    
    print "bar = [$bar], baz = [$baz]\n";
    

    If we run this, it dies with:

    Bareword "baz" not allowed while "strict subs" in use at tprot.pl line 13.

    That's a compile-time error caused by strict: the compiler saw the symbol baz and didn't know what it was, because the typeglob *Foo::baz does not get altered until runtime. But bar worked fine because it was defined in a BEGIN block, which executes immediately during compilation.

    IOW, because barewords are ambiguous, Perl needs to know at compile-time whether it's a sub or something else. So you can install these during import (which executes in an implicit BEGIN block) but not at runtime.

    Additionally, prototypes affect compilation semantics; a constant subroutine (like those made by constant.pm) gets optimized away. Other prototypes cause the parser to change its behavior (e.g. subs which can take code blocks.) The compiler has to know about all this stuff before calls to the sub are actually encountered in the code, so they can be parsed correctly. The code runs after everything is already parsed.

    Calling a sub with explicit parens or with an ampersand does not have this restriction because Perl is smart enough at runtime to know that these are subroutine calls, and to look them up dynamically in the symbol table.