Ich kann jede Methode auf Nil aufrufen und das fühlt sich falsch an

14

Ich habe in letzter Zeit viel Zeit damit verbracht, ein Skript zu debuggen, und als ich schließlich das Problem fand, lag es an Code, der so aussah:

class Foo {
    has $.bar;
    method () {
        # do stuff
        $!.bar;
    }
}

Es stellte sich heraus, dass das Problem damit bestand $!.bar, was entweder $!baroder sein sollte $.bar. Ich verstehe das.

Aber warum stirbt das nicht ?

Mit Blick auf diese im Detail, es sieht aus wie das Problem hier ist , dass ich versuche , eine (nicht vorhanden) Methode aufrufen barauf $!, die an dieser Stelle ist , Nilweil es irgendwelche Fehler nicht gewesen sein.

Und es sieht so aus, als könnte ich tatsächlich jede Methode aufrufen, die ich möchte, Nilund alle kehren lautlos zurück Nil, einschließlich Sachen wie Nil.this-is-a-fake-methodund Nil.reverse-entropy(123).

Ist das eine Funktion? Wenn ja, was ist der Grund?

jja
quelle

Antworten:

13

Es ist beabsichtigt und dokumentiert, ja. Die Überschrift für Nillautet "Fehlen eines Wertes oder eines harmlosen Fehlers", und in der Klassendokumentation wird erwähnt

Jeder Methodenaufruf Nileiner nicht vorhandenen Methode und folglich jede Subskriptionsoperation ist erfolgreich und kehrt zurück Nil.

say Nil.ITotallyJustMadeThisUp;  # OUTPUT: «Nil␤» 
say (Nil)[100];                  # OUTPUT: «Nil␤» 
say (Nil){100};                  # OUTPUT: «Nil␤»

In Synopsis 2 heißt es: "Jeder undefinierte Methodenaufruf bei NilRückgaben Nil, so dass NilMethodenaufrufketten nach unten weitergegeben werden. Ebenso jede Subskriptionsoperation bei NilRückgaben Nil". Daher scheint die Absicht darin zu bestehen, Ausdrücke wie " $foo.Bar()[0].Baz()ohne Überprüfung Nilbei jedem Schritt" oder "Nil-safe" zuzulassen "Methodenaufruf- und Subskriptionsoperatoren.

Hobbs
quelle
4
Tatsächlich. Und das schon schon vor langer Zeit: github.com/rakudo/rakudo/commit/174727377f (Dezember 2013) Siehe auch die dazugehörigen Spekulationen: design.raku.org/S02.html#Nil
Elizabeth Mattijsen
1
@ ElizabethMattijsen ah, ich denke S02 hat die Antwort. "Jeder undefinierte Methodenaufruf bei NilRückgabe Nil, sodass NilMethodenaufrufketten nach unten weitergegeben werden." Es ist also beabsichtigt, dass Sie $foo.Bar().Baz().Blah()bei jedem Schritt keine Nulltests oder eine spezielle ?.Art von Operator benötigen (solange Sie am Ende mit Null richtig umgehen). Ich werde das bearbeiten, danke.
Hobbs
1
A Nil, das keine Fehler macht und nur mehr Nilzurückwirft, wird in Obj-C und den NS-Frameworks, wo es auf ähnliche Weise verwendet wird, ziemlich umfangreich verwendet - ermöglicht verkettete Anrufe, die weiterhin Nil weiterleiten. Ich kenne die genauen Leistungseinbußen von Ausnahmen und deren Erfassung nicht, aber ich vermute, dass die NilVerkettung effizienter, wenn auch weniger flexibel sein kann.
user0721090601
1
(aber das ist eine völlig uninformierte Vermutung, daher freue ich mich, dass lizmat oder jnthn mir sagen, dass ich falsch liege und dass ich den
Mund
2
Die NilKlasse verfügt über eine Methode FALLBACK docs.raku.org/language/typesystem#index-entry-FALLBACK_(method) , die zurückgegeben wird Nil. Grundsätzlich wird kurz bevor eine Ausnahme ausgelöst wird, weil eine Methode nicht gefunden werden konnte, eine Überprüfung FALLBACKdurchgeführt und aufgerufen, falls verfügbar.
Elizabeth Mattijsen
5

Diese Frage (und die Antwort von Hobbs) hat mich auch ... unruhig gemacht: Ich habe schließlich https://docs.raku.org/language/traps gefunden : Es erklärt, dass das Zuweisen Nilnormalerweise einen anderen Wert erzeugt Any. Die folgende grundlegende REPL-Interaktion zeigt dies ebenfalls:

> 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

((Der Unterschied zwischen =und :=wird hier behandelt: https://docs.raku.org/language/containers#Binding ))

... also ist die allgemeine Idee, dass der "fehlende Wert oder gutartige Fehler" im regulären Code weit weniger verbreitet ist, als ich (und vielleicht auch Sie?) plötzlich befürchtet habe: Es tritt jedoch auf, wenn Sie mit Regex arbeiten stimmt direkt und in Ihrer speziellen zufälligen Situation mit dem direkten Aufrufen von Methoden überein $!.

Dies machte mich bewusster über den Unterschied zwischen Nilund Anyund die bereitgestellte Begründung machte für mich mehr Sinn.

Chromis
quelle
2
NilSetzt einen Container auf seinen Standardzustand. Sie können den Standardwert ändern. my $foo is default(42) = 5; $foo = Nil; say $foo; # 42
Brad Gilbert