Search code examples
perlvariablesinternalslexical-scope

Trying to localize an outside package variable through a lexical binding in Perl


It's a long title, but I'm afraid I can't take a single word out without losing the true meaning of the question. I'll give a quick description of what I'm trying to achieve first, then a long rambling on why I want it done this way. Skip the rambling if you intend to directly answer abiding with the question title :-)

Quick description

Assuming the existence of a lexicalize builtin that does just what I want, here goes:

package Whatever;
{
  lexicalize $Other::Package::var;
  local $var = 42;
  Other::Package->do_stuff; # depends on that variable internally
}

Whys and whynots

For a bit of context, I'm whitebox-testing a third-party module.

I want the variable localized because I want it changed for a limited time only, before the tests move on to something else. local is the best way I found to do this without depending on knowing the module's choice for an initial value.

I want a lexical binding for two main reasons:

  1. I don't want to pollute the test file's higher-level namespace.
  2. I want something shorter than the fully-qualified name. It's not in the sample code for brevity, but I use the identifier a lot more than what's shown, with calculations and updates relative to its previous value.

I couldn't decently use our because it won't grab a variable from another package: “No package name allowed for variable $Other::Package::var in "our".” Cheating to temporarily enter Other::Package's scope doesn't cut it: if I use a block ({ package Other::Package; our $var }) then the binding doesn't last long enough to be useful; and if I don't (package Other::Package; our @var; package main) then I need to know and copy the previous package's name, which prevents moving that piece of code around too much.

While doing my homework before asking a previous form of this question, I discovered Lexical::Var, which seemed like it would be exactly what I needed. Alas: “Can't localize through a reference.”

I've also tried my best on my gut feeling of my *var-based forms, but kept bumping into syntax errors. I've learned more than I cared to about scoping and binding today :-)

I can't think of a reason why what I want shouldn't be possible, but I can't find the right incantation. Am I asking for an unfortunate unimplemented edge case?


Solution

  • I am not quite sure if I understand you correctly. Does this help?

    #!/usr/bin/env perl
    
    {
        package This;
        use warnings; use strict;
        our $var = 42;
    
        sub do_stuff {
            print "$var\n";
        }
    }
    
    {
        package That;
        use warnings; use strict;
    
        use Lexical::Alias;
        local $This::var;
    
        alias $This::var, my $var;
    
        $var = 24;
        print "$var\n";
    
        This->do_stuff;
    }
    
    package main;
    
    use warnings; use strict;
    
    This->do_stuff;