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

1

7

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.

Hypogastrium answered 12/10, 2016 at 0:40 Comment(1)
Alright, I updated my answer.Owings
O
7

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.

Owings answered 12/10, 2016 at 4:54 Comment(4)
Wouldn't my $alias = Some::Really::Long::Package::; work regardless of if the package definitions are all in one file? In practice I'd put them in different files, I just simplified the situation to pose the question succinctly.Hypogastrium
Not sure if I understand you right. In your example where all package calls are in one file, the scalar $alias (declared with my inside Short) could be seen by all packages below (e.g. main), because it's lexical scope is the entire file. As this is not what you want, I limited the scope of $alias with a {} block in the last example. If your packages are in their own files, my will make the variable completely private to it's package and thus inaccessible from all other/external packages.Owings
I found why it doesn't work with :: in the perl source. When doing a lookup via Alias::myMethod(), parse_gv_stash_name is called with a null stash pointer, which causes perl to use the default stash, which is main.Hypogastrium
To be honest, I didn't check the perl source. Thanks for pointing that out! I'm glad to see that my statement is consistent with the source.Owings

© 2022 - 2024 — McMap. All rights reserved.