July 2012 Archives

Golfing for Gotchas

I’ve been building a little stand alone command line tool lately, which led to me looking at using App::FatPacker to make a standalone, single-script download. This was going well until I tried to load Digest::Perl::MD5, which caused fatpacker to mysteriously crash with an undefined value. The reason for this is interesting…

When fatpacker goes to analyze a module list, it at one stage runs require on all of them, like so:

require $_ for @packages;

Then later on it uses @packages and discovers that one of the elements has is now undef. How did this happen?

Well, if the module you require fiddles with $_ without localizing it first, that will ultimately result in modifying @packages. How did Digest::Perl::MD5 do this?

while (<DATA>) {

It read its data block at load load time. And of course, the last value in $_ there is undef. This all would have been avoided had the require loop been written out to be less golfy:

for my $package (@packages) {
    require $package;
}

Of course, this is the same function that had:

my %found;@found{map+($pack_rev{$INC{$_}}||()),@targets}=();

And anyone who would publish code with that in it is probably beyond hope. ;)

(Bugs with patches have been filed with both App::FatPacker and Digest::Perl::MD5. The latter, to just localize $_ before the while loop.)

Localizing Variables in Coroutines

While I was browsing the CPAN the other day, I came across Coro::LocalScalar and I said to myself, oh, what a useful idea… it lets you localize globals to a thread. An important feature if you do anything in your thread with those pesky globals from perlvar, for instance.

But, said I, its calling convention is the rather gross Coro::LocalScalar->new->localize($scalar); and it’s implemented using tie and only for scalar values. It would be nice to have a new keyword and support for arrays and hashes as well. As it happens, I’d also recently run across Begin::Declare, which is a Devel::Declare based module that provides custom variable declarations.

A few hours later, with the example of Begin::Declare to work from, I bring you Coro::Localize (now on a CPAN mirror near you). It’s localization is achieved through a combination of Coro’s on_enter /on_leave blocks and Data::Alias.

So you might use it this way:

our $scalar = "some value";

async {
    corolocal $scalar = "thread local";
    ...
};

$scalar will be set to "thread local" for all of the code inside the async block, even if you cede and someone else fiddles with the variable. Similarly, any code outside the async block will only ever see "some value".

About Rebecca

user-pic I blog about Perl.