Search code examples
perlfunctioninheritanceperl-moduletypeglob

Inheriting a function with typeglobs


Does the first example with the typeglobs have disadvantages compared to the second example?

package Some::Module::Win32;
use strict;
use 5.10.0;

use Exporter 'import';
our @EXPORT_OK = qw(public);

use Some::Module;

*_func_one   = \&Some::Module::_func_one;
*_func_two   = \&Some::Module::_func_two;
*_func_three = \&Some::Module::_func_three;

sub public {
    my $s = _func_one();
    for my $i ( 0 .. $s ) {
        say _func_two( $i );
    }
    say _func_three( $s );
}

1;

package Some::Module::Win32;
use strict;
use 5.10.0;

use Exporter 'import';
our @EXPORT_OK = qw(public);

use Some::Module;

sub public {
    my $s = Some::Module::_func_one();
    for my $i ( 0 .. $s ) {
        say Some::Module::_func_two( $i );
    }
    say Some::Module::_func_three( $s );
}

1;

Solution

  • Your first example kind of shows how Exporter works: by assigning typeglobs. But there is an important difference: when the functions are imported. This is mostly important when a subroutine has a prototype. Prototypes need to be known during parsing, and therefore have to be known in the BEGIN phase. use – which usually calls import on the used package – is processed in the BEGIN phase.

    You should also realize that in your first example, users of your code can now Some::Module::Win32::_func_one() whereas this isn't possible in the 2nd example (unless Some::Module exports _func_one.

    Here is a version that imports the function in the correct phase:

    package Some::Module::Win32;
    use strict;
    use 5.10.1;  # because `parent` comes with 10.1
    
    use parent 'Exporter';  # optimal way to inherit the `import`.
    our @EXPORT_OK = qw(public);
    
    use Some::Module;
    
    BEGIN {
      *_func_one   = \&Some::Module::_func_one;
      *_func_two   = \&Some::Module::_func_two;
      *_func_three = \&Some::Module::_func_three;
    }
    
    sub public {
        my $s = _func_one();
        for my $i ( 0 .. $s ) {
            say _func_two( $i );
        }
        say _func_three( $s );
    }
    
    1;
    

    And if you want to, you can use a module like namespace::autoclean to remove any foreign imports from the symbol table once they are no longer needed by you.

    Another strategy would be to put the coderefs into lexical variables. However, the syntax is a bit ugly, and any prototypes are completely ignored:

    package Some::Module::Win32;
    use strict;
    use 5.10.1;
    
    use parent 'Exporter';
    our @EXPORT_OK = qw(public);
    
    use Some::Module;
    
    my $func_one   = \&Some::Module::_func_one;
    my $func_two   = \&Some::Module::_func_two;
    my $func_three = \&Some::Module::_func_three;
    
    sub public {
        my $s = $func_one->();
        for my $i ( 0 .. $s ) {
            say $func_two->( $i );
        }
        say $func_three->( $s );
    }
    
    1;
    

    This does not touch the symbol table, and could therefore be considered a very “clean” solution.

    Your second version may be more verbose, but it has the advantage of being very explicit, which may make the code easier to understand for a reader or maintainer. It is the solution I often end up using.

    • prototypes work correctly
    • very readable (unless the full name of the function is insanely long, like Marpa::R2::Inner::Scanless::G::SYMBOL_IDS_BY_EVENT_NAME_AND_TYPE)
    • unambiguous
    • no namespace pollution