Subtle Bug When Using Readonly

Ovid on 2007-02-02T10:35:34

The code originally had this:

my $CurrentUser = $ENV{SUDO_USER} || getpwuid $>;

Since that should be a constant and I was adding other constants, I changed it to this:

use Readonly;
Readonly my $CURRENT_USER => $ENV{SUDO_USER} || getpwuid $>;

Do you see the bug? It's a fatal error and three people read through the code and missed it (which is really no excuse for me writing it in the first place). Fortunately, it's an easy fix.


Precedence?

Dom2 on 2007-02-02T13:35:03

Just looking at it, I'd guess that it's a precedence issue and you need some parens thrown in. This is what I get from B::Deparse.

  % perl -MO=Deparse -MReadonly -e 'Readonly my $CurrentUser = $ENV{SUDO_USER} || getpwuid $>'
  Type of arg 1 to Readonly::Readonly must be one of [$@%] (not scalar assignment) at -e line 1, at EOF
  -e had compilation errors.
  use Readonly;
  &Readonly(my $CurrentUser = $ENV{'SUDO_USER'} || getpwuid $>);

So, not what I expected, but still an error.

-Dom

Re:Precedence?

Ovid on 2007-02-02T13:45:01

It's a context error. Consider the first line:

my $CurrentUser = $ENV{SUDO_USER} || getpwuid $>;

That must be scalar context (in fact, the || forces the left side to be in scalar context). However, consider the new version:

Readonly my $CURRENT_USER => $ENV{SUDO_USER} || getpwuid $>;

We have a list, not an assignment, so we have list context. getpwuid $> in list context generates more than one value and Readonly recognizes that we're trying to use a scalar, so it dies. The fix is to force scalar context:

Readonly my $CURRENT_USER => $ENV{SUDO_USER} || scalar getpwuid $>;

Side note, because || forces the left side to be in scalar context, you can get strange results:

perl -le 'print  qw<a b c> || 4'

That prints 'c', but guess what the following prints:

perl -le 'print  qw<a b 0> || qw<1 3 7>'

That prints '137'. The left side is forced into scalar context and a list in scalar context returns the last item, but the right side is in list context. The following, of course, prints '3', because arrays behave differently from lists in scalar context:

perl -le 'my @x = qw<a b 0>; print  @x || qw<1 3 7>'

Confused yet? :)

Re:Precedence?

jjore on 2007-02-02T21:10:29

I'm not so sure "lists in scalar context" is what you mean. The comma operator in scalar context does that and qw(...) looks like a funny kind of list that might use the comma operator.

Nasty

Aristotle on 2007-02-02T13:47:21

This is probably one of those times to say “Perl 6 will fix that.”

(Besides providing for such as “is ro.”)