Excitement level: 3/5 and rising.
This strikes
me as a source of hard-to-find and relatively easy-to-generate bugs. Given all
the other stuff that has been deprecated out of Perl over the years, I am
surprised this hasn’t been tagged for deprecation. Especially given that,
according to
a comment from preaction, the single-quote as a package separator was
considered archaic as of Perl 5.6.
I got the
error “Use of uninitialized value in concatenation (.) or string…”, and after a
bit of testing, I discovered that “$package'varname” is
apparently an alias of “$package::varname” (in my case,
Perl was trying—and failing—to print $user::s).
Did everyone else know this? This is literally the first time I have run across this in almost 20 years of Perl programming
(Of course a quick Google search turns this up in the opening paragraphs of the perlmod docs—I wonder if it’s time to read all of that stuff cover-to-cover?)
our $MAX_ATTEMPTS = 5;
sub lookup_lot {
# Some terrifying code goes here. It
throws an
# exception on all errors including
timeouts.
}
sub lookup_lot_with_retry {
our $attempts = 0;
do {
our $result = eval { lookup_lot(@_)
};
if ($@) {
# Exceptions besides timeouts are re-thrown.
die $@ if $@ !~ m/:[ ]Timed[ ]out[ ]/;
}
else {
return $result; # no error means
success
}
# The call to lookup_lot() timed out;
try again.
} while (++$attempts <
$MAX_ATTEMPTS);
die "Failed operation after $attempts
attempts\n";
}
All that is fine, because it’s actually the corrected version I deployed. In the previous version, $attempts was accidentally declared in a global
scope, right below where $MAX_ATTEMPTS was set. Did I mention the program that contains this
code run continuously for months at a time, tracking in lot after lot after
lot? All this led to the following log entry:
2017-03-02,16:23:12,ERROR,Failed operation after 49892 attempts
Oops.
That said, are both of the following allowed? Is there a difference in execution between them? Is the second nonsensical and should be disallowed?
while ($condition) {
loop body
}
continue {
continue block
}
else {
else block
}
vs.
while ($condition) {
loop body
}
else {
else block
}
continue {
continue block
}
I agree this would be very useful when paired with grep (or map)
Any language feature can be abused and sure, you could use this to create confusing and nonintuitive code. You can also write whole scripts that executed in the evaluation part of s///e, but that doesn't mean you should. To your specific point regarding goto I think this idea is a lot different from goto because it CANNOT be used to jump around arbitrarily, or into or out of a block. It is very limited in scope.
It does affect the code after the block, so I can see an argument that it is "spooky action at a distance" but I think it's pretty intuitive what's going on... really it's just syntactic sugar for
$loop_entered = 0;
foreach $item (@list) {
$loop_entered = 1;
loop body
}
if (not $loop_entered) {
else clause
}
As to your point that else is a Boolean term, well, that's what's happening. Either the loop happened or else invoke this code. If you are more concerned about overuse of a keyword, then what do you think would be a better keyword to use? How about "skipped?"
…with a for/foreach loop:
for my $temperature ($temp_lower_bound ..
$temp_upper_bound) {
set_temperature($temperature,
$soak_time);
push @data, get_sensor_reading();
}
else {
die "Temperature upper bound must
be greater than lower bound!\n";
}
foreach my $datum (@data) {
print "processing
$datum...\n";
process($datum);
}
else {
warn "No data!\n";
}
…with a while loop:
open LOG_FILE, "<", $log_file
or die "Could not open log file:
$!\n";
while (<LOG_FILE>) {
process_log_entry($_);
}
else {
print "The log file was
empty!\n";
}
close LOG_FILE;
Obviously all of these examples could be implemented by
wrapping the loop in an if…then…else that checks the condition or boundaries, or by setting a
“ran once” flag in the body and checking it after, but those solutions—that I
use all the time—seem unwieldy and inelegant to me.
Note this isn’t a Perl-specific concept; it could apply
to almost any procedural programming language. (Does it already exist anywhere
else?)
{
"ferrari"
: {
"method": "ssh", "ipaddr":
"10.26.101.92"
},
"yugo"
: {
"method": "rsh", "ipaddr":
"10.25.34.112"
}
}
In the script,
he looked up the client in question, and put its configuration into variables
like $method. So far, so good. But here’s the code that
decides which access method to use:
if (method == "ssh") {
do_ssh($ipaddr, $command);
}
else {
do_rsh($ipaddr, $command);
}
So this code omits the sigil on $method—problem #1—and thus treats method as a bareword (i.e. the literal string "method"), then compares that to the literal string "ssh", but does so NUMERICALLY—problem #2. Since both are non-numeric strings, they are both treated as 0, and the comparison is always true. Needless to say, this script did not turn on warnings or strict mode—problem #3. Luckily, the vast majority of our critical systems required ssh, and on those systems, three wrongs made a “right!”