TL;DR Yes, first
returns Nil
when no values match. One way to get what you want is to bind the result (with :=
) instead of assigning it (with =
).
A simple solution
Switch from the assignment operator (=
) to the binding operator (:=
):
sub MAIN()
{
my @myNumberList is Array[Int] = [2, 2, 5, 7] ;
my $result := @myNumberList.first( {$_ > 10} ) ; # <-- Add colon
say '$result' ;
say $result ;
say $result.WHAT ;
say ($result === Nil) ;
} # end sub MAIN
displays:
$result
Nil
Nil
True
What's happening in your original code
In Raku, doesn't routine first
return Nil
when no values match?
Yes. That's not the problem.
Nil
represents "Absence of a value ...".¹
So don't think of it as a value like, say, 42
or 'string'
or an empty list².
Assigning Nil
to a variable resets the variable's value to its default:
my $foo is default(42) = Nil;
say $foo; # 42
The default default value for a variable is Any
³, so:
my $bar = Nil;
say $bar; # (Any)
That's what happened in your original code.
Options
You could set $result
's default to Nil
so that when you assign Nil
to it you get what you want:
my $result is default (Nil) = @myNumberList.first( {$_ > 10} ) ;
But that's cumbersome. And seldom desirable.
Usually, if you want a variable to keep a Nil
, you just bind it instead of assigning it:
my $qux := Nil; # <-- use `:=` instead of `=`
say $qux; # Nil
Just set your mental model to match what Raku's doing and you'll find it's all really simple.⁴
Footnotes
¹ Per its doc, Nil
is "Absence of a value or a benign failure". The "Absence of a value" aspect applies if you call first
to find the first value in some list of values that matches a predicate, but there were no such values. The "benign failure" applies if you call first
and it encounters some error condition and either decides it's benign but ought terminate its processing, or throws an exception that gets handled such that further evaluation continues with the code that receives the value returned by first
.
As you read this footnote you might think these two cases would best be handled separately. But if you think further you will hopefully see that they work beautifully as two sides of the same coin. Any called code can return Nil
, and calling code knows that the result of the call is an absence of a value. This may be due to entirely normal processing which yields no value, or due to processing that encountered an error, but that error was either considered by the called code as so benign as to not be worth reporting to the caller other than by returning Nil
, or resulted in an exception being thrown that was subsequently handled to clear the error condition -- thus returning processing to normal status. Raku's elegant and practical Nil
is yet another reason why I ❤ Raku.
² An empty list is a still a value. Albeit an empty one.
³ Any
is a type. When treated as a "value" it's a type object representing an undefined value of type Any
or a subtype thereof. It makes a natural default default value for variables and parameters.
⁴ I suspect I offended folk by writing my "Just ..." sentence. I thought about deleting or editing it, but then folk wouldn't have context for @jubilatious1's comment and my point, which I think is a good one that @jubilatious1 misunderstood, would be lost. I'm leaving it in with this footnote for now in the hope someone will engage with me if they think it is a problem and/or if it would be a good idea for me to remove it or, conversely, unstrike it.