"use constant" versus ReadOnly

Ovid on 2005-08-30T19:13:10

I just blew a nice chunk of time debugging the following problem:

use constant DEFAULT_ARGS => [qw/some data/];

...

unless (@_) {
    $self->{args} = DEFAULT_ARGS;
}

Later on in my code, I had this:

my $args = $self->args;
while ( defined (my $curr_arg = shift @$args) ) {
    # do something while blithely ignoring 
    # the havoc we're wreaking
}

See the bug? The two accidentally coupled lines of code were 214 lines apart and appeared to be unrelated. That was a bugger to track down. The fix I used was to simple clone those args when needed, but my longer-term fix is to not use constants. Use ReadOnly:

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper::Simple;
use ReadOnly;

use constant FOO => [qw/bar baz/];
my $data = FOO;
shift @$data;
print Dumper(FOO);

Readonly::Scalar my $foo => [qw/bar baz/];
$data = $foo;
shift @$data;
print Dumper($foo);

And the output:

$FOO = [
  'baz'
];
Modification of a read-only value attempted at constant.pl line 15


Constant?

HollyKing on 2005-08-30T20:58:55

That doesn't seem very constant. Thanks for the tip.

Great minds think alike.

Louis_Wu on 2005-08-30T21:09:30

Damian says the same thing in Perl Best Practices. And for pretty much the same reason.

Re:Great minds think alike.

Ovid on 2005-08-30T21:40:09

And mediocre minds steal from great ones. Where do you think I learned about the Readonly package? :)

Re:

Aristotle on 2005-08-31T05:03:07

I don’t know if the real code did something significantly different, but to me that’s just an example against consuming when you merely need to iterate. Or more abstractly: “avoid side effects.” I can’t remember ever having run into this sort of issue.

I always cringe when I see people use shift on @_ – the only time I do that is when I really intend to modify the array, which boils down to my $self = shift; and precious little else.

Re:

Ovid on 2005-08-31T18:02:25

I didn't show all of the code. There's a relatively common idiom of treating an array like a set of pairs, or an ordered hash:

while (defined (my $key = shift @array)) {
  my $val = shift @array;
  # do something
}

In this case, this data needs to be represented as an array (hence my not using an ordered hash) except in this one odd corner case. Due to the nature of the code, this must always be the last step, so I didn't think destroying the array would be bad. I don't like it and it's definitely a code smell but it has to do with the way we receive external requests. Another option is to iterate over the array by 2:

for (my $i = 0; $i < @array; $i += 2) {
  my ($key, $value) = @array[$i, $i + 1];
  # do something
}

I suppose the latter syntax is not as bad as I thought, but I hate C-style for loops as it's so easy to write them incorrectly.

Re:

Aristotle on 2005-08-31T19:14:56

Ah – yeah, stepping through an array in steps of two is annoyingly complicated, and I admit to having used consumption to iterate in those cases as well:

while( my ( $k, $v ) = splice @array, 0, 2 ) {
    # ...
}

And you’re right about C-style loops, I avoid them like the plague myself.

Hmm, what would be a good idiom to establish for the purpose… maybe this?

for my $i ( 0 .. $#array / 2 ) {
    my ( $k, $v ) = @array[ $i * 2, $i * 2 + 1 ];
    # ...
}

Cumbersome… but the best that can be done in Perl 5, I think?

It works the same for any set size if you just change the factor, of course; if you want to ignore an incomplete set at the end of the array, then the loop must run only to @array / 2 - 1.

Constant versus ReadOnly

Alias on 2005-08-31T15:29:24

I guess the big question is, why make it a constant? The big reason, so far as I can tell, is compile-time optimisation.

If you can define constants such that they will be compiled out, and in doing so make your code smaller, faster or simpler, then constants are a good thing.

use constant UNIX = ...;

eval UNIX ? 'END_UNIX' : 'END_OTHER';

etc
etc

And so now you have smaller faster unix-specific code paths...

Re:Constant versus ReadOnly

Alias on 2005-08-31T15:30:05

Yuck, it killed my << chars :(

Re:Constant versus ReadOnly

Aristotle on 2005-08-31T18:47:48

Use the ecode tags, Luke. :-)