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

I want to know the reason why I have to use defined when I need to know whether a variable is defined instead of using direct comparison with undef.
For example this code isn't work as I expected

#!/usr/bin/env perl my($str,$empty_str,$number,$number_zero)=('Hi','',1,0); if($str!=undef){print"\$str is defined\n"} if($empty_str!=undef){print"\$empty_str is defined\n"} if($number!=undef){print"\$number is defined\n"} if($number_zero!=undef){print"\$number_zero is defined\n"}

output: $number is defined

Replies are listed 'Best First'.
Re: why can't compare a variable with undef directly?
by Eily (Monsignor) on Dec 10, 2014 at 09:08 UTC

    Most of the time, the way perl works will just make your work easier, and Do What You Mean. For exemple, in C, if you have an int with value 42, and the string "42", you have to convert either one of them to the other format. The == operator (and all numeric operators) makes it simpler by implicitly converting both sides to numbers, this way you don't have to care if it was user input, extracted from a string, the output of an operation on other numbers or another source, you can just do 42 > "40" and perl will understand what you mean and compare 42 and 40.

    Perl is good at guessing most of the time, but it's not psychic, so when you try undef == 0, it will use the implicit meaning of undef ("nothing"), and compare "nothing" to 0, and tell you that's the same thing. That's why you have to write explicitly that you want to know wether a variable is defined or not.

      Extending what Eily said, eq and the other string comparison operators, impose string context to their operands. This also will not work.

      defined is the only operator that reliably tests for undef.

Re: why can't compare a variable with undef directly?
by Anonymous Monk on Dec 10, 2014 at 07:55 UTC
    Because operators == and != force numeric context on their operands.

    Add use warnings to your code and it will start working properly

    Use of uninitialized value in numeric ne (!=) at w.pl line 8. Argument "Hi" isn't numeric in numeric ne (!=) at w.pl line 8. Use of uninitialized value in numeric ne (!=) at w.pl line 9. Argument "" isn't numeric in numeric ne (!=) at w.pl line 9. Use of uninitialized value in numeric ne (!=) at w.pl line 10. Use of uninitialized value in numeric ne (!=) at w.pl line 11. $number is defined
Re: why can't compare a variable with undef directly?
by Anonymous Monk on Dec 10, 2014 at 07:39 UTC

    I want to know the reason why I have to use defined when I need to know whether a variable is defined instead of using direct comparison with undef. For example this code isn't work as I expected ...output: $number is defined

    Why do you expect the wrong thing? Where do you get your perl information from? Where did you learn that "" is the same as undef?

    "" is never undef, its ""

Re: why direct undef comparison isn't working
by blindluke (Hermit) on Dec 10, 2014 at 07:42 UTC

    When you 'compare variables', you compare their values. undef means that there is no useful value to compare.

    Still, it's a warning, not an error. Look at the following example. It's not something very useful, but it shows that if you really want to, you CAN compare undef.

    #!/usr/bin/perl use warnings; use v5.14; { no warnings 'uninitialized'; if ('ssd' ne undef) { say "The strings are not equal"; } } { no warnings 'uninitialized'; if ('' eq undef) { say "The strings seem equal"; } }

    Output:

    The strings are not equal The strings seem equal

    Edit: updated for clarity, added the example

    - Luke

Re: why direct undef comparison isn't working
by MidLifeXis (Monsignor) on Dec 10, 2014 at 14:16 UTC

    The value undef means that there is no defined value. If two somethings have not been defined, that does not make them equal.

    Consider how you would get from point A to point B, and point A to point C. Until you know where they are, road conditions, method of travel, number of passengers, and so on, the details of travel to both locations are undefined. That does not make them equal.

    If you squint hard enough, it can be compared to a NULL value in SQL.

    --MidLifeXis

Re: why direct undef comparison isn't working
by wazat (Monk) on Dec 10, 2014 at 16:32 UTC

    The != operator performs a numeric comparison. Perl has built-in magic that gives scalars both numeric and string interpretations. It does the best it can at providing useful defaults when a veriable doesn't have a useful numeric equivalent.

    'Hi' defaults to the number zero.

    undef also defaults to zero.

    When you use != against undef you are doing a numeric comparison against zero. Of your variables, only 1 is not numerically equal to zero.

    You could try string comparison, eq, but that won't solve your problem either. As a string undef is equivalent to the empty string.

    Using defined does exactly what you want.

    Using defined also indicates that you intentionally dealing with the case where a variable might not be initialized. Uninitialized variables are an endless source of bugs. That us why you should have warnings turned on. Similarly, use strict; helps prevent a lot of other bugs.

    Your program spits out warnings, if warnings are turned on

    #!/usr/bin/perl use warnings; my($str,$empty_str,$number,$number_zero)=('Hi','',1,0); if($str!=undef){print"\$str is defined\n"} if($empty_str!=undef){print"\$empty_str is defined\n"} if($number!=undef){print"\$number is defined\n"} if($number_zero!=undef){print"\$number_zero is defined\n"}

    Output:

    Use of uninitialized value in numeric ne (!=) at ./delme.perl line 7. Argument "Hi" isn't numeric in numeric ne (!=) at ./delme.perl line 7. Use of uninitialized value in numeric ne (!=) at ./delme.perl line 8. Argument "" isn't numeric in numeric ne (!=) at ./delme.perl line 8. Use of uninitialized value in numeric ne (!=) at ./delme.perl line 9. $number is defined Use of uninitialized value in numeric ne (!=) at ./delme.perl line 10.
Re: why direct undef comparison isn't working
by Anonymous Monk on Dec 10, 2014 at 07:45 UTC
Re: why can't compare a variable with undef directly?
by Anonymous Monk on Dec 10, 2014 at 13:24 UTC
    undef is rather like NULL in a database context ... it is "the absence of a value." Any value. Comparison to nothing-at-all has no meaning. But it is valid to test for exists($a) == exists($b), which is True if both are defined or if neither one is.

      Actually that is invalid because exists doesn't operate on scalars.

      exists argument is not a HASH or ARRAY element or a subroutine at ...

      The valid equivalent of that is defined $a && defined $b but don't use $a and $b outside of a sort.

        Actually, exists tests if the element is present. Even if the element exists, the value of the element could still be undef.

        Note that defined($array[$i]) will cause the element at index $i to be created. Likewise, defined($array{$k}) will create an element referred by key $k. In both cases, the new element with have the value undef.

        This "auto-vivification" may have undesirable side effects. This is most likely to become a problem where a hash is being used to hold options or named parameters. Often times, the absence of an option or parameter has a different meaning than when present with no value specified (ie, value is undef).

        When in doubt, it is generally better to test for existence then for a defined value:

        if (exists $params{'foo'}) { if (defined $params{'foo'}) { ...; } else { ...; } } else { ...; }
        Oops ... defined(), obviously. duh.