Home > Back-end >  In Raku, doesn't routine first return Nil when no values match?
In Raku, doesn't routine first return Nil when no values match?

Time:07-02


According to Raku documentation, routine first returns Nil when no values match. However, why doesn't the line say ($result === Nil) ; (below) print True?

sub MAIN()
  {
  my @myNumberList is Array[Int] = [2, 2, 5, 7] ;
  my $result = @myNumberList.first( {$_ > 10} ) ;
  say '$result' ;
  say $result ;
  say $result.WHAT ;
  say ($result === Nil) ;
  } # end sub MAIN

Program output is …

$result
(Any)
(Any)
False

Update

My goal was to determine whether a list or an array contains one or more elements satisfying a given predicate. In the following sample program, the elems routine returns a value that is greater than 0 if and only if the given list or array contains at least one element satisfying the predicate given in the corresponding call to grep .

sub MAIN()
  {
  my $myList = (2, 2, 5, 7) ;
  say '$myList' ;
  say $myList ;
  say $myList.WHAT ;

  my $grepResult1 = $myList.grep( * > 10, :p ) ;
  say '$grepResult1' ;
  say $grepResult1 ;
  say $grepResult1.WHAT ;
  say ($grepResult1.elems <= 0) ;

  my $grepResult2 = $myList.grep( * > 4, :p ) ;
  say '$grepResult2' ;
  say $grepResult2 ;
  say $grepResult2.WHAT ;
  say ($grepResult2.elems <= 0) ;

  my @myArray = [2, 2, 5, 7] ;
  say '@myArray' ;
  say @myArray ;
  say @myArray.WHAT ;

  my $grepResult3 = @myArray.grep( * > 10, :p ) ;
  say '$grepResult3' ;
  say $grepResult3 ;
  say $grepResult3.WHAT ;
  say ($grepResult3.elems <= 0) ;
  } # end sub MAIN

Program output is …

$myList
(2 2 5 7)
(List)
$grepResult1
()
(Seq)
True
$grepResult2
(2 => 5 3 => 7)
(Seq)
False
@myArray
[2 2 5 7]
(Array)
$grepResult3
()
(Seq)
True

CodePudding user response:

Interesting question that appears to be (somewhat) addressed on the "Traps To Avoid" documentation page. See the specific subheading: "Assignment of Nil can produce a different value, usually 'Any' ". Also, the Raku documentation page on class Nil .

The short answer--as near as I can surmise--is to use smartmatching with a Junction on the right-hand-side:

Sample Code:

sub MAIN()
  {
  my @myNumberList is Array[Int] = [2, 2, 5, 7] ;
  my $result = @myNumberList.first( {$_ > 10} ) ;
  say '$result' ;
  say $result ;
  say $result.WHAT ;

"\n".say;

  say  Nil ~~ all($result);  #True
  say  Nil ~~ any($result);  #True
  say  Nil ~~ one($result);  #True
  say  Nil ~~ none($result); #False

"\n".say;

  my $result2 = @myNumberList.first( {$_ < 10} ) ;
  say  Nil ~~ all($result2);  #False
  say  Nil ~~ any($result2);  #False
  say  Nil ~~ one($result2);  #False
  say  Nil ~~ none($result2); #True

  } # end sub MAIN

Returns:

$result
(Any)
(Any)


True
True
True
False


False
False
False
True

Alternatively, you can simplify the above code (and clean up your output) using .defined:

  1. for result above (where there are no matching values) say $result if $result.defined; returns nothing;

  2. for result2 above (where the first matching value is 2) say $result2 if $result2.defined; returns 2.

For further reading to understand how the smartmatch solution(s) posted above came about, look at the following Github issue: "In general, the left side of ~~ should not be auto-threading".

[Also, (at the risk of confusing the reader), take a look at the Raku code posted on the following Perl5 doc page: https://perldoc.perl.org/perlsyn#Differences-from-Raku ].

ADDENDUM: For a discussion on the 'do-something-only-if-defined' idiom in Raku, see Damian Conway's talk "On The Shoulders of Giants", which includes his code for a user-defined ?= ("assign-if-defined") operator.

  • Related