tags:

views:

109

answers:

3
+3  A: 
sub example {
  my $var1 = shift || '';
  my $var2 = shift || 'something';
  my $var3 = shift || '';

  return "var1 = ".$var1.", var2 = ".$var2.", var3 = ".$var3;
}
M42
Note that, in Perl 5.10 or later, you can use `//` rather than `||` to allow for passing in values of 0 or empty strings - `//` tests whether the value is *defined*, while `||` tests whether it's *true*. `0` and `''` are defined, but false.
Dave Sherohman
+3  A: 
  • I think the PBP (Perl Best Practices) is a good read on a "strict" style. If you follow what it says, you will at least be writing code compliant with strict and warnings.

What I see is uninit warnings. And, uninit warnings come with warnings, not strict. Though it usually goes hand in hand with strict.

For your particular error, it's important to realize that when you shift the arg array (which I have a tendency to call "deck") that the user of the method/function might not have passed anything in that position. Thus this is a good idiom;

my $var1 = shift || '';

'' is not uninitialized, and so won't get the warning. Ternaries are also a good means and I use them enough in my production code.

Of course, there's another issue though. If you take a look at the code above, what am I expecting? I'm expecting that $var1 might not have been passed any data. If that is the case, and I still want to stringify it--and there are enough cases like this in the local code--sometimes you just do this:

# don't bug me about uninitialized stringed values
# just concatenate them
no warnings 'uninitialized'; 
return "var1 = ".$var1.", var2 = ".$var2.", var3 = ".$var3; 

It's equivalent code to doing this with a bunch of variables:

my $var = shift || '';

The idea is not to be taken by surprise with uninitialized data--having a policy that fits the instance, rather than slavishly coding to a standard.

Since your sub is easy enough, the above code will work. But you probably want to make it do more in the future, so I suggest the following construct to isolate the parts that we're okay with uninitialized values and where you don't want to be caught unaware.

{   no warnings 'uninitialized';
    $string = ...;
}
$obj_that_should_be_defined->do_something_objecty( $string );

do blocks are also useful for this.

$obj_that_should_be_defined->do_something_objecty( 
    do { no warnings 'uninitialized'; sprintf( ... ); }
);

Of course anything that you pass as a variable, you can also filter with a map. The following works if you just want to take care of undefs.

$object->method( map { defined() ? $_ : '' } @anon_args );  
  • I good word of warning is that anything that hasn't already been checked in the scope--anything coming from somewhere else: parameter, IO, regex capture, returns from functions--can be uninitialized. The practice of good coding should have you checking all these things IF you care in that instance whether they are undef of not.

Now, a lot of times people check for undefined variables just to croak. Since Perl's messages have gotten better, and perhaps if they could improve in the future. It might not be necessary to croak specifically.

my $feldman = Feldman->new( qw<here is some data for you> )
    or die 'Could not create Feldman!'
    ;
$feldman->dont_just_sit_there_do_something();

However without the explicit or die, the code above will definitely die if $feldman is undefined. You can't call a method on an undefined value, so there is case for not specifically checking. If $feldman is undefined, then you didn't create it, there is just one more level of inference you have to make from undefined to the error message. Neither tells us why the object was not created, just that it wasn't. (The constructor should at least have carped about something it didn't have.)

Axeman
Note, once again, the mangled syntax highlighting.
Svante
@Svante, yup, as they say "Only Perl parses Perl"
Axeman
+1  A: 

I would second the recommendation to look at Perl Best Practices. Damian Conway is a great teacher. Also bear in mind that the book is not intended to be read dogmatically, and even suggestions that you don't adopt are worth thinking over.

For positional arguments that all take the same default value, I often do something like this:

sub example {
    my ($var1, $var2, $var3) = map { defined() ? $_ : '' } @_;
}

Even more flexible, however, is to write functions that use named arguments instead. This approach has several benefits, one of them being the ease with which default values can be handled:

sub example {
    my %DEFAULTS = ( foo => 0, bar => '', fubb => 'blah' );

    # When the hashes are merged, user-supplied arguments
    # will trump the defaults.
    my %args = (%DEFAULTS, @_);
}

example(foo => 123);

If I recall correctly, PBP discusses this trick of merging hashes, which is useful in many contexts.

FM