I spent considerable time debugging a script recently, and when I finally found the problem it was because of code that looked like this:
class Foo {
has $.bar;
method () {
# do stuff
$!.bar;
}
}
It turned out the problem was with that $!.bar
, which should have been either $!bar
or $.bar
. I get this.
But why doesn't this die?
Looking at this in more detail, it looks like the issue here is that I'm trying to call a (non-existent) method bar
on $!
, which at this point is Nil
because there haven't been any errors.
And it looks like I can actually call any method I want on Nil
and they all silently return Nil
, including stuff like Nil.this-is-a-fake-method
and Nil.reverse-entropy(123)
.
Is this a feature? If so, what's the rationale?
It's intended and documented, yes. The headline for Nil
is "Absence of a value or a benign failure", and the class documentation mentions
Any method call on
Nil
of a method that does not exist, and consequently, any subscripting operation, will succeed and returnNil
.say Nil.ITotallyJustMadeThisUp; # OUTPUT: «Nil» say (Nil)[100]; # OUTPUT: «Nil» say (Nil){100}; # OUTPUT: «Nil»
Synopsis 2 states "Any undefined method call on Nil
returns Nil
, so that Nil
propagates down method call chains. Likewise any subscripting operation on Nil
returns Nil
", so the intent seems to be allowing expressions like $foo.Bar()[0].Baz()
without requiring checks for Nil
at every step, or special "Nil-safe" method call and subscripting operators.
This question (and hobbs' answer) also made me feel... uneasy: I eventually found https://docs.raku.org/language/traps: it explains that assigning Nil
produces a different value, usually Any
. The following basic REPL interaction also demonstrates this:
> my $foo = Nil
(Any)
> $foo.bar
No such method 'bar' for invocant of type 'Any'
in block <unit> at <unknown file> line 1
> my $bar := Nil
Nil
> $bar.baz
Nil
(( the difference between =
and :=
is covered here: https://docs.raku.org/language/containers#Binding ))
...so the general idea is that the 'absent value or benign failure' is far less widespread in regular code than I (and perhaps you as well?) suddenly feared: it does occur however, when you are going to work with regex matches directly and in your particular accidental situation related to directly invoking methods on $!
.
This made me more aware about the difference between Nil
and Any
, and the provided rationale made more sense to me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With