Search code examples
perlbooleancomparisonlogical-operatorslogical-or

Comparing two undefs in Perl


It seems comparison undef ne undef returns undef. Does Perl have idiomatic way to convert it to perls "boolean"? Also I find following to be strange:

undef ne undef || 0; # 0
(undef ne undef) || 0; # undef

Could you explain, please, what is happening?


Solution

  • It seems comparison undef ne undef returns undef.

    It doesn't.

    ne returns either the special scalar true (known as &PL_yes internally) or the special scalar false (&PL_no), never undef. In this particular case, it returns false.

    $ perl -e'
       use v5.36;
       use experimental qw( builtin );
       use builtin qw( is_bool );
    
       sub inspect {
          !defined( $_[0] ) ? "[undef]" :
          is_bool( $_[0] ) ? ( $_[0] ? "[true]" : "[false]" ) :
          $_[0]
       }
    
       my $x = undef ne undef;  # Line 12
       say inspect( $x );       # [false]
    '
    Use of uninitialized value in string ne at -e line 12.
    Use of uninitialized value in string ne at -e line 12.
    [false]
    

    (The results are the same in earlier versions of Perl, but is_bool was only introduced in Perl v5.36.)

    false is a scalar that contains the integer zero, the float zero and the empty string.

    $ perl -e'
       use v5.36;
       use experimental qw( builtin );
       use builtin qw( false );
    
       {  my $x = "";     say 0+$x;  say "[$x]"; }  # Line 6
       say "--";
       {  my $x = 0;      say 0+$x;  say "[$x]"; }
       say "--";
       {  my $x = false;  say 0+$x;  say "[$x]"; }
    '
    Argument "" isn't numeric in addition (+) at -e line 6.
    0
    []
    --
    0
    [0]
    --
    0
    []
    

    I find following to be strange:

    That's because it's not true. The two snippets are 100% equivalent, and they both return 0.

    $ perl -e'
       use v5.36;
       use experimental qw( builtin );
       use builtin qw( is_bool );
    
       sub inspect {
          !defined( $_[0] ) ? "[undef]" :
          is_bool( $_[0] ) ? ( $_[0] ? "[true]" : "[false]" ) :
          $_[0]
       }
    
       {  my $x =   undef ne undef   || 0;  say inspect( $x );  }  # Line 12  # 0
       {  my $x = ( undef ne undef ) || 0;  say inspect( $x );  }  # Line 13  # 0
    '
    Use of uninitialized value in string ne at -e line 12.
    Use of uninitialized value in string ne at -e line 12.
    0
    Use of uninitialized value in string ne at -e line 13.
    Use of uninitialized value in string ne at -e line 13.
    0
    

    (The results are the same in earlier versions of Perl, but is_bool was only introduced in Perl v5.36.)


    Could you explain, please, what is happening?

    ne is a string comparison operator. So it starts by casting its operands into strings. Converting undef to a string produces the empty string, and emits a warnings. Since the empty string is equal to the empty string, ne returns a false value, and specifically the special false scalar.

    Since false is a false value, || evalutes and returns its right-hand side value (0).


    Does Perl have idiomatic way to convert it to perls "boolean"?

    The simplest way to get true or false from a value is to negate it twice.

    my $normalized = !!$x;
    

    The simplest way to get 1 if true or 0 if false is to use the conditional operator.

    my $normalized = $x ? 1 : 0;