A very stupid, over-clever scoping-based importing trick
In some code I’m working on, I use a module which exposes a whole bunch of package variables as part of its public interface. It does not export them, however. I could refer to them directly by prefixing them all with Some::Module::
, but that would be ugly and verbose. It’s also unsafe – the vars
stricture will not help you catch typos if you use fully qualified names.
The obvious solution would be to emulate what exporting does:
our ($bar, @baz, %quux);
*bar = \$Some::Module::bar;
*baz = \@Some::Module::baz;
*quux = \%Some::Module::quux;
But this forces me to mention every single variable name thrice, just so I can declare it.
Another option would be to just use references stored in lexicals:
my $bar = \$Some::Module::bar;
my $baz = \@Some::Module::baz;
my $quux = \%Some::Module::quux;
That does remove one point of repetition. But it does so in exchange for having to strew arrows or double sigils all over the code.
Either way, the package name must be repeated over and over and over. This is possible to fix with a fairly innocuously stupid trick:
our ($bar, @baz, %quux);
(*bar, *baz, *quux) = do {
package Some::Module;
\($bar, @baz, %quux);
};
But this is, if anything, uglier than before. It does, however, allow for a nice improvement under recent perls (namely, given the refaliasing
feauture):
\our ($bar, @baz, %quux) = do {
package Some::Module;
\($bar, @baz, %quux);
};
That’s not so bad any more.
But it’s still repetitive. It’s not nice enough that I would use it when I’m taking a shortcut, and it’s not obvious enough that I would use it when I’m aiming for maintainability.
It is possible to go further, though.
Do you know what our
does? Do you know exactly? Let me tell you: it declares a lexically scoped variable that aliases the package variable of the same name. Packages and lexical scopes are orthogonal concepts. The current package can change any number of times within a scope. And a lexical variable declared during a scope will be visible during the entire rest of the scope, regardless of the current package at any given point. Which means…
package Some::Module;
our ($bar, @baz, %quux);
package main;
… this works. After changing the current package to the one we want, lexically scoped aliases declared with our
refer to package variables in that package. When we then switch the current package back, we remain in the same scope, so the lexicals we just declared remain visible.
The only irritating part is that the switch back has to explicitly name the package, something that was not necessary with the do { package ...; ... }
construct above. Unfortunately we cannot block-scope the package switch like that, because that would also block-scope the lexicals, and we need them to survive. That was, after all, the whole point of the exercise.
So it can’t be made perfect, but it’s very close: every identifier involved (the package name and each variable of interest) is named exactly once, with exactly one redundant mention of an identifier not relevant to the intention of the code.
Clearly this… technique is too obscure to use in code that needs maintenance. But in throw-away scripts it might just come in handy.
Leave a comment