Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

When warnings get in the way

by Sprad (Hermit)
on Apr 22, 2005 at 16:37 UTC ( [id://450481]=perlquestion: print w/replies, xml ) Need Help??

Sprad has asked for the wisdom of the Perl Monks concerning the following question:

I have code like this:

if ($foo eq "true") { ...

This does what I want. If $foo is "true", it runs. If it's anything else, or not set at all, it doesn't. But -w throws warnings at me if $foo isn't defined. To satisfy it, I have to do this instead:

if ($foo && $foo eq "true") { ...

The first check will short-circuit if it fails, so the warning doesn't get thrown. But it seems like I'm making my ifs unnecessarily complex. Is there a better/prettier way?

---
A fair fight is a sign of poor planning.

Replies are listed 'Best First'.
Re: When warnings get in the way
by halley (Prior) on Apr 22, 2005 at 16:59 UTC
    This isn't in direct answer to your question, but may help you to code in a "more Perlish" way, and have to work less to get the answers you want.

    The traditional value for "falsehood" in Perl is undef. When converted to a string, that looks like "", and when converted to a number, that looks like 0, and when converted to a list, that looks like (); all three are also considered false. The special string "0" is also considered false, because it would convert to a false value if treated as a number. These are the only values considered false, by the way.

    The traditional value for "truth" in Perl is 1. Actually, anything that is not false is considered true, so your string "true" is suitable, but perhaps overkill and misleading too.

    Coding to expect truth or false by comparing against a specific value may mislead your readers. Use specific values when they're important, but use 1/undef when you just want to keep a boolean flag value. Code your conditionals to just look at that flag, and not compare that flag to some specific value. Getting in that habit will help you avoid introducing errors when the variable is true but not equal to "true" for example.

    Consider coding this way:

    $foo = 1; if ($foo) { print "Yes, it's true.\n"; } if (not $foo) { print "No, it's false.\n"; } $foo = undef; if ($foo) { print "Yes, it's true.\n"; } if (not $foo) { print "No, it's false.\n"; }

    --
    [ e d @ h a l l e y . c c ]

Re: When warnings get in the way
by davidrw (Prior) on Apr 22, 2005 at 16:53 UTC
    The workaround seems fine -- it's straightforward and legible. You could probably make sure that $foo is never undef, too, so you don't have to worry about it. Something like:
    my $foo = getFoo() || ''; if ($foo =~ /bar/ ){ ... }
    But this has limitations (like if getFoo() returns 0).

    side note #1 -- might just be your example, but treating foo as a boolean instead of string matching for "true" might work better, and avoids the warning. something like (again, depends on what getFoo() can return):
    my $foo = getFoo() ? 1 : 0; if ( $foo ){ ... }

    side note #2 -- I'm sure someone will mention it if it's true, but i think perl6 has some new operators that can handle things like that (but i might be mis-remembering).
    Update: I was thinking of (found it here) this:
    $a // $b; # short for: defined($a) ?? $a :: $b $pi //= 3; # so could do (of course perl6 only): if( $foo // '' eq "true" ){ ... } # or my $foo = getFoo(); $foo //= ''; if( $foo eq "true" ){ ... }
Re: When warnings get in the way
by itub (Priest) on Apr 22, 2005 at 17:56 UTC
    I have an ambivalent relationship with "uninitialized" warnings, because they result in false positives and get in the way more often than not. Sometimes I just start my script with:
    #!/usr/bin/perl use strict; use warnings; no warnings 'uninitialized';

    But for maximum safety you should leave them on, or just turn them off at a smaller scope.

      Yup, quite agree with that. For instance when grabbing data from a database, some columns may be empty, though initializing the hash before fetching the data would be a real pain. So I often use something like

      no warnings; print Dumper(\%bighash); use warnings;

      but your

      no warnings 'uninitialized';

      is a neat trick, maybe I'll try this one next time.

Re: When warnings get in the way
by Old_Gray_Bear (Bishop) on Apr 22, 2005 at 17:05 UTC
    my $foo = " ";
    Add six characters, three of them blank; there you go. It's not much of an effort, but in six months the maintenance programmer (and that might be you) is going to appreciate it.

    By initializing the variable to a known state, you have told the Maintainer what you thought about the potential values that $foo can contain, and explicitly selected one of them as the default. Developers who think about the small things are more likely to be thinking about the bigger issues as well. It has been my unfortunate experience that people who do not initialize their variables often have other unpleasant coding habbits.

    ----
    I Go Back to Sleep, Now.

    OGB

      That (and there was a similar suggestion above as well) doesn't help his problem.
      my $foo = ' '; # or my $foo = ''; $foo = getFoo(); if ( $foo eq "true" ){ ... } # this will warn if $foo is undef $foo = $dbh->selectrow_array("select x from blah limit 1"); if ( $foo eq "true" ){ ... } # this will warn if $foo is undef
      In these cases, the original default value doesn't matter because it's overwritten later by a 'third party' (some sub or dbh call), and the hint to the Maintainer doesn't help cause it's something else (e.g. some db value) that's being stored.

        $foo = getFoo() || "";

        I really need to start paying attention.
        -derby
Re: When warnings get in the way
by jZed (Prior) on Apr 22, 2005 at 17:12 UTC
    I will sometimes use this kind of construct if I have reasons for not defining $foo earlier.
    if ( ($foo||'') eq "true") { ...
Re: When warnings get in the way
by holli (Abbot) on Apr 22, 2005 at 16:54 UTC
    Just define your variables. Use my $foo = ""; instead of my $foo;


    holli, /regexed monk/
Re: When warnings get in the way
by kwaping (Priest) on Apr 22, 2005 at 17:28 UTC
    As with most things in Perl, there are many ways to accomplish this task (avoiding the "uninitialized" error). Personally, the solution I use is contextual. When doing form data validation where there can be a lot of fields, and all the field names may not neccessarily be known by the programmer, I like to use the OP's method:
    if ($form{$key} && $form{$key} =~ /\w+/) { # ...
    However, when checking data passed to a method or subroutine, I prefer to use the ||= (aka "default") method:
    my ($self,$asdf) = @_; $asdf ||= '';
    Finally, in general, I try to scope, declare, and initialize my variables at the start of the program whenever possible.
Re: When warnings get in the way
by ambrus (Abbot) on Apr 22, 2005 at 17:31 UTC

    You can turn on a certain kind of warning for a block or function, like

    if (do { no warnings "uninitialized"; $foo eq "true" }) { ... }
    IMO, there is nothing wrong with disabling a warning, as long as you do it only to certain warnings, not all (like no warnings;), and only for the part of code where the warning is ok. This is especially important with warnings such as no warnings "exiting"; for which there is no such easy way to avoid.

    In this case however, I see nothing wrong with the explicit $foo && test, I do not think it is really bad. For a more comprehensible code, I'd write something like

    if (defined($foo) && $foo eq "true") { ... }
    or
    if (($foo || "") eq "true") { ... }
    or even
    if ((defined($foo) ? $foo : "") eq "true") { ... }
Re: When warnings get in the way
by dragonchild (Archbishop) on Apr 22, 2005 at 17:35 UTC
Re: When warnings get in the way
by salva (Canon) on Apr 24, 2005 at 12:45 UTC
    for a one time construction I would use
    if (defined $foo and $foo eq 'true') {...}
    but if I needed to code similar tests in several places I would go for:
    sub is_true ($) { defined $_[0] and $_[0] eq 'true' } if (is_true $foo) {...} if (is_true $bar) {...}

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://450481]
Approved by Old_Gray_Bear
Front-paged by polettix
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (7)
As of 2024-04-16 09:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found