September 15, 2004
The frustration of aliasing Long::Package::Names in Perl
A huge annoyance of Perl is that it doesn't distinguish between namespaces and class names. Instead of having a class Student in a namespace Entitiy::Human, you can only declare a package named Entitity::Human::Student, cramming both parts into one huge, ugly... thing.
This design flaw truly begins to rear its ugly head farther down the code, when you must use the gruesomely long Entity::Human::Student whenever you need to instantiate the class, or want to call a static function.
In Java, C#, Ruby or Python you could have a piece of code simply import the namespace Entity::Human and then work with the class name Student. So I thought it would be nice for the next version of Reformed Perl to allow something like this:
package Class; import Entity::Human; my $student = Student->new(); Student->show_all();
Implementing this however has turned out to be a rather frustrating affair.
Attempt 1: Naively aliasing package names
My first idea was to alias the package name Entity::Human::Student to the short Student, using the method employed by Joshua Keroes's Package::Alias or Michael King's import pragma:
*{'Student::'} = *{'Entity::Human::Student::'}
The fatal disadvantage of this method is that package aliases created this way are global. Not only the current package can refer to Entity::Human::Student as Student, but every package in your project.
Given a second package List::Sorter::Student and two spots in your project importing the namespaces Entitity::Human and List::Sorter respectively, no one knows what the alias Student would resolve to. In any case one place in your project is now getting the wrong package.
Attempt 2: Don't alias, catch errors
My next hack was to not alias anything at all, and catch the error message through $SIG{__DIE__}. If a piece of code would die crying Can't locate object method "foo" via package "Student"' I could simply redirect the request for Student->foo to it's correct home at Entity::Human::Student->foo, right?
Redirecting the call isn't a problem, but there doesn't seem to be a way to prevent the script from dying anyway afterwards.
Attempt 3: Alias through constants
The third approach was to alias the package names through constants, as in:
use constant Student => 'Entity::Human::Student';
This way Student->foo() would equal Entity::Human::Student->foo() and the alias would be local to a given package! But my delight didn't last long: Because constants in Perl are really just subroutines with constant return values it isn't possible to define a constant at runtime! Thus, if you created your alias at runtime, Student->foo() wouldn't resolve to anything else. You could write Student()->foo(), but that's all but inacceptable.
Excuse me while I shoot myself in the head.
Comments
Although I don't have a deep understanding of this problem, there seems to be another problem with Perl's namespaces that you didn't mention: there's no parent-child relationship.
For example, consider the following:
package Foo::Bar;
sub hello { print "hello\n" }
package Foo;
Bar::hello(); # error - Bar::hello() doesn't exist
Foo::Bar::hello(); # OK
If this worked, at least some of the problem would disappear. You could refer to your Student class just as a Student at least within it's parent package.
Posted by Matt Ryall (#)
perl -le'{ package UNIVERSAL; sub AUTOLOAD { print our $AUTOLOAD }; } Foo->bar'
But I remember warnings that overloading &UNIVERSAL::AUTOLOAD has its own set of perils, though I can't recall any off the top of my head. On the other hand, you've already succumbed to the use of source filters, so you may not care that much anyway.
Posted by Aristotle Pagaltzis (#)
Aristotle: Thanks.
Posted by
Henning
(#)
Couldn't you do something like the localy scoped subroutines?
i.e.,
local *Student = sub { $PACKAGE_NAME };
??
Posted by Daniel (#)
Daniel: You cannot locally alias packages like Student::Freshman this way.
Posted by
Henning
(#)