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
Nilof 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