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

Hi,

this is probably really simple.
I am returning a numeric value from a database and want to trap it if it is null. So what i do at the moment is:

if ($id eq undef){
   ###deal with id
}else{
   ###deal with not undef id
}

What that does, works however it keeps throwing 'Use of uninitialised values in string eq' warnings, and i would like to be able to get rid of them, using an undef safe idea.

Any ideas?

Thanks -- Joe.

Replies are listed 'Best First'.
Re: String equality and undef
by JavaFan (Canon) on Dec 09, 2008 at 12:56 UTC
    Well, considering that 'undef', is, uhm, an "unintialised value", and that you use it as an argument to 'eq', I am not at all surprised at the warning.

    Now, you could turn off warnings for this case; clearly you intended to use an undefined value, so there's no point for Perl to warn you.

    However, comparing the 'undef' can be the wrong thing. In string context (and that's what the operands of 'eq' will be in), undef becomes the empty string. So, if $id can be an empty string, you might get the wrong thing.

    You can solve both issues by using

    if (!defined $id)
    as your first test.
Re: String equality and undef
by ccn (Vicar) on Dec 09, 2008 at 12:18 UTC
    Test the value before use

    perldoc -f defined

        if( $foo ){ ... }

        You cannot do this as the value returned may be 0 or the empty string both of which are not true:

        perl -e 'use DBI;$h = DBI->connect("dbi:ODBC:xxx","xxx","xxx");eval {$ +h->do("drop table mje")};$h->do("create table mje (a int null)");$s = + $h->prepare("insert into mje values(?)");$s->execute(0);$s->execute( +undef);$r = $h->selectall_arrayref("select * from mje");foreach (@$r) +{print "$_->[0]\n" if $_->[0];}'

        prints exactly nothing. You need to use defined. You may also find the following useful http://www.easysoft.com/developer/languages/perl/dbd_odbc_tutorial_part_2.html#nulls.

Re: String equality and undef
by webfiend (Vicar) on Dec 09, 2008 at 14:28 UTC

    If you follow the very good advice of using defined as the test, you might also want to switch the ordering of your if/else logic. Instead of

    if (!defined $id) { # Deal with id } else { # Deal with not undef id }

    you might want to consider

    if (defined $id) { # Deal with a defined id } else { # Deal with an undefined id }

    It's just personal aesthetics. The first form ("if not" / "else") sounds awkward to me compared to "if" / "else".

      On the other hand, dealing with a bogus value is usually shorter than what you do with a real value, and I think a good style guideline is to keep "else" as close as possible to the condition it negates.

      if ( ! defined $id ) { die "Why is this ID not defined?" } else { # Twenty # or # more # lines }

      I like this guideline because I've too many times found myself looking at a screen of code like this:

      # blah # blah } else { return undef; } } else { unlink $tmpfile; return undef; }

      What were the conditions?

      Even better in this case would have been an early return so as to avoid the extra level of indent anyway, but that's beside the point.

        Fair point, but you don't really need to worry about the else when your reaction to the if condition is death.

        unless ( defined $id ) { die "Why is this ID not defined?"; } # Life goes on normally for everyone else.

        And I disagree with your closing, an early return in cases like that is exactly the point. You want to take any steps you can to make the code more readable, especially if it means completely ignoring my initial "switch the logic" comment.