perlperl-stash

How do I alias a long package name without affecting the main package?


If I have a really long package name, I can alias that package by making an entry in the symbol table:

BEGIN {
    # Make "Alias" be an alias for "Some::Really::Long::Package";
    *Alias:: = \*Some::Really::Long::Package::;
    # Equivalent to:
    # *main::Alias:: = \*Some::Really::Long::Package::;
}

This is what something like Package::Alias does for you internally. However, this stinks because it mucks with the main package. How can I make the alias only affect the current package, and be able to use just the alias within the package? I tried changing the alias definition to

*Short::Alias:: = \*Some::Really::Long::Package::;

But then I have to use Short::Alias->myMethod() instead of just Alias->myMethod().

use strict;
use warnings;

package Some::Really::Long::Package;

sub myMethod {
    print "myMethod\n";
}

package Short;

BEGIN {
    # Make "Alias" be an alias for "Some::Really::Long::Package";
    *Short::Alias:: = \*Some::Really::Long::Package::;
}

# I want this to work
Alias->myMethod();

package main;

# I want this to not work
Alias->myMethod();

Bonus points if both Alias->myMethod() and Alias::myMethod() work in the Short package, and not in main.


Solution

  • Since you don't want to modify the symbol table of main, you could create a "package-local" alias with

    package Short;
    
    use constant Alias => 'Some::Really::Long::Package'; # create alias
    Alias->myMethod(); # correctly calls myMethod() of package Some::Really::Long::Package
    

    or alternatively

    package Short;
    
    sub Alias { Some::Really::Long::Package:: } # create alias
    Alias->myMethod(); # correctly calls myMethod() of package Some::Really::Long::Package
    

    Both examples should work as intended. Consequently, any attempt to call Alias->myMethod() from package main will obviously fail with

    Can't locate object method "myMethod" via package "Alias" (perhaps you forgot to load "Alias"?)

    because the symbol table %main:: or %:: has no such entry called Alias::.


    Due to the fact that you have several package calls in one file, you can also create $alias to reference Some::Really::Long::Package. Then you can limit the scope of $alias to the package Short to make it inaccessible from other places:

    package Short;
    {
       my $alias = Some::Really::Long::Package::; # create alias
       $alias->myMethod(); # correctly calls myMethod() of package Some::Really::Long::Package
    }
    

    EDIT (as response to the edited question):

    The updated question is:

    Can both Alias->myMethod() and Alias::myMethod() work in the Short package but not in main?

    I don't think so.

    There are several options to make it work with the -> syntax but not with the ::. This is because perl seems to assume that a bareword like Alias followed by the package separator :: represents the package Alias in the package main:

    package Short;
    
    Alias::myMethod();           # if you call myMethod(), you actually call it ...
    
    ::Alias::myMethod();         # ... like this, which is equivalent ...
    
    main::Alias::myMethod();     # ... to this
    

    All three calling options are equivalent, which demonstrates an important fact. If perl encounters something like Foo::, it starts looking in the package main first and not from the current (relative) location. So if I'm not missing something here, you can not use Alias::myMethod() without adding Alias:: to main.

    This is what something like Package::Alias does for you internally. However, this stinks because it mucks with the main package.

    Now, what I described above would also explain why modules like Package::Alias modify your main package, because there seem to be no way to avoid it.