Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 516
Chris Avatar asked Oct 12 '16 00:10

Chris


1 Answers

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.

like image 101
reflective_mind Avatar answered Sep 22 '22 09:09

reflective_mind