-
When Magics Collide
2007-01-12 21:00 in /tech/perl/HallOfShame
I haven’t posted a Hall of Shame entry in a while, and this one adds a new twist. Today, it’s perl itself that gets the blame. (I know, some will say that’s always the case.) This is about when two magics collide.
There are a couple magic tokens that you can use in perl scripts to represent the current context, like
__PACKAGE__
and__LINE__
. These tokens behave differently from variables, and one example of this is that they don’t interpolate inside strings.For reasons of backwards compatibility, when perl encounters a bareword that it doesn’t recognize, it ‘auto-quotes’ it (treats it as a string). This is generally considered A Bad Thing and the
strict
pragma forbids it. Except for when it doesn’t, and that exception is hash keys. So, even withstrict
, a bareword hash key is magically auto-quoted.Now, consider this code:
package foo; use strict; my %h; $h{__PACKAGE__} = "bar"; while (my ($k, $v) = each %h) { print "$k => $v" };
I think it’s clear that anyone writing this code expects the output to be
foo => bar
Unfortunately, it’s actually
__PACKAGE__ => bar
What’s happened is that perl has auto-quoted the string before doing the token substitution. In order to get the desired behavior, it’s necessary to resort to atrocities like
$h{__PACKAGE__ . ""} = "bar";
or
$h{+__PACKAGE__} = "bar";
to get the job done
-
Fast Startup Times
2005-12-21 17:04 in /tech/perl
There’s a bit of a misconception in a recent perl.com article about startup times of perl scripts. The author asserts that the time spent finding and loading modules in a long PERL5LIB would be reduced if Perl was a compiled language. This is generally untrue, as anyone who has straced a C program as it starts up and seen the barrage of stats and opens when dynamically loaded libraries are searched for in LD_LIBRARY_PATH. True, you can statically link a compiled program, but it’s pretty rare in my experience.
-
Exponential Growth is Fast
2005-04-27 22:05 in /tech/perl/HallOfShame
The following is a good way to use up all your memory amazingly quickly:
my $special_chars = "!@#$%^&"; ... sub munge { ... $special_chars = quotemeta($special_chars) ... }
[Aside: I’ve come to realize that a risk in this “Hall of Shame” business is that coworkers might take offense. So, let me make a comment or two. First off, a healthy fraction of these examples are actually things that I wrote at one point, so I do try to be fair. Second, my intent really is to teach, and maybe amuse occasionally, not to ridicule; I hope that someone, somewhere will avoid stepping into the same trap.]
-
use vars
, dammit!2005-03-18 11:00 in /tech/perl/HallOfShame
Or
our
if you prefer. Whatever you do, don’t refer to variables in the current package using the full, explicit package name. Concretely, don’t say,@My::Package::ISA = qw( My::SuperClass );
Instead, write
our @ISA = qw( My::SuperClass );
or
use vars qw( @ISA ); @ISA = qw( My::SuperClass );
The problem with the first is that it is typo-prone. If you typo the package name,
perl -cw
won’t complain (in fact, perl will happily do exactly what you said), but you’ll get errors like ‘Can’t locate object method “foo” via package “My::Package”’. This is particularly likely to happen when you create a new subclass and copy the old subclass to use as a template. The alternatives are typo-proof, and easier to read to boot.(BTW, this is a specific case of the general principle Don’t Repeat Yourself. In this case, you already typed the package name once in the
package
directive. Doing it again anywhere else in the file is just asking for trouble sometime later.) -
Can you spot the bug?
2005-02-03 13:49 in /tech/perl/HallOfShame
Can you spot the bug in this code?
use strict; use constants { STATE_A => 0, STATE_B => 1, STATE_C => 2, }; ... if ($state == STATE_A) { do_a(); } elsif ($state == STATE_B) { do_b(); } elsif ($state == STATE_C) { do_c(); } else { die("bad state: $state"); }
(more)
-
Useless Use of $_
2004-09-23 20:58 in /tech/perl/HallOfShame
A new feature — my Perl Hall of Shame, featuring occasional snippets of perl code that someone actually wrote. Today, a little pattern I’ll call “useless use of $_”:
for ( 0..9 ) { my $i = $_; for ( 0..9 ) { my $j = $_; ... } }
I think that perhaps the author of this was trying to avoid polluting the enclosing lexical scope with his loop variables, but wasn’t aware that exactly the same thing can be achieved without resorting to punctuation variables:
for my $i ( 0..9 ) { for my $j ( 0..9 ) { ... } }
-
Benchmarking I/O
2004-08-18 19:11 in /tech/perl
Okay, it totally sucks that Benchmark only looks at CPU time. Honestly, how often is CPU the bottleneck in a real systems? Granted, it’s pretty simple to write an alternative benchmarking tool for wallclock time, but that’s just all the more reason that it ought to be available as an option in the standard package.
-
Brainbench
2004-07-14 22:03 in /tech/perl
We’re contemplating ditching our home-grown test for job applicants and requesting they take one or more Brainbench tests instead. But, of course, we need to evaluate those tests and get some normalization. So, I took an hour this morning and took the Perl test. I am now officially a Perl Master. Whoot.
The test was fairly decent, although I was surprised how few, and how simple, the regular expression questions I got were. There were only about three or four and they were almost complete just about whether you know about maximal vs. minimal matching. I got a lot more questions about symbol tables and globs than I expected. I guess maybe it focuses in on that type of thing if it decides you are pretty good (the test is adaptive), although that’s the sort of thing that I think should only be used with extreme caution, even by experts. I also got one or two weird questions where I’m half-convinced there were typos or mis-wordings in the question. Unfortunately, I didn’t think to note them down at the time.
Addendum: I forgot to mention that there were a number of questions along the lines of, what does this script output, or, why does this fail? The rules for the test say that you can use reference materials, physical or online. They don’t explicitly forbid just running the code, but it seems like that shouldn’t be allowed. I restricted myself to Programming Perl and perldoc, but it was definitely tempting to just run the code a couple of times. This seems like a concern if we really want to use this for assessing candidates. I felt like I ought to email Brainbench and ask for clarification about this.
-
Questioning Standard Idiom
2004-06-30 16:29 in /tech/perl
I think I’ve decided to jettison the popular
ref $that || $that
idiom from my Perl objects. To some extent, I’m just being silly because what’s put me over the edge is the fact that you basically can’t get complete test code coverage for this construct, so my Devel::Cover reports are doomed to stay short of 100%.However, the fact of the matter is that I never make use of this “feature” of my objects. For that matter, I don’t think I’ve ever seen anyone else use this feature either. To put the final nail in, it’s a completely false analogy to C++ copy constructors. No copy is made — it’s just another default constructor.
So, no more will I use this incantation, unless I’m actually creating a real copy constructor.
-
Breaking News -- Unsafe Signals, Unsafe
2004-03-04 14:03 in /tech/perl
Using unsafe signals in Perl 5.8.1 and later can have some bizarre consequences. For example, this code:
my $time; $time = `date +%H:%M:%S`; chomp $time; print "BEFORE [$time] ( $/ = ", unpack('H*', $/), " )\n"; eval { local $SIG{'ALRM'} = sub { die 'timeout' }; alarm(2); my $out = `sleep 5`; alarm(0); }; $time = `date +%H:%M:%S`; chomp $time; print "AFTER [$time] ( $/ = ", unpack('H*',$/), " )\n"; $/ = "\n"; my $foo = "blah\n"; chomp $foo; print "FOO [$foo] ( $/ = ", unpack('H*',$/), " )\n";
Produces the following:
%% PERL_SIGNALS=unsafe ./chompfail.pl BEFORE [13:38:49] ( $/ = 0a ) AFTER [13:38:51 ] ( $/ = 0a ) FOO [blah] ( $/ = 0a )
With safe signals, it's fine (but doesn't interrupt the sleep.) Also, with perl's
sleep
function it works fine. And, in the perl debugger, it seems to work fine.I'm not sure what's going on here (since it isn't as simple as
$/
being set strangely), but I think the clear lesson is that unsafe signals are to be avoided if you don't want to risk undefined behavior after the signal, which you probably don't if you're running a long-lived server process.