Comparing two undefs in Perl

162 Views Asked by At

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?

2

There are 2 best solutions below

2
ikegami On BEST ANSWER

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;
0
brian d foy On

First, undef ne undef does not return another undef. I think you probably had something else going on:

$ perl -lE 'say defined(undef ne undef) ? q(Defined) : q(Undefined)'
Defined

Here's a more involved program that shows what Perl is looking at underneath the hood:

use Devel::Peek qw(Dump);
use v5.10;

my $null;
Dump($null);

my $result = undef ne undef // 17;
Dump($result);
say defined $result ? "Defined! <$result>" : "Not defined"

The output shows that an undef value is different than a PL_no value (see @ikegami's answer):

Defined! <>
SV = NULL(0x0) at 0x7f980f8163a0
  REFCNT = 1
  FLAGS = ()
SV = PVNV(0x7f980e8098b0) at 0x7f980f82d590
  REFCNT = 1
  FLAGS = (IOK,NOK,POK,IsCOW,pIOK,pNOK,pPOK)
  IV = 0
  NV = 0
  PV = 0x101e9cac5 "" [BOOL PL_No]
  CUR = 0
  LEN = 0

Also note that the value in $result is not 17 as it would be if the left hand side of the defined-or // were undef. It's merely false.