Posso chiamare qualsiasi metodo su zero e questo mi sembra sbagliato


14

Di recente ho passato molto tempo a eseguire il debug di uno script e, quando finalmente ho riscontrato il problema, è stato a causa di un codice simile al seguente:

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

Si è scoperto che il problema era quello $!.bar, che avrebbe dovuto essere $!baro $.bar. Capisco questo.

Ma perché questo non muore ?

Guardando a questo più in dettaglio, sembra che il problema qui è che sto cercando di chiamare un metodo (inesistente) barsu $!, che a questo punto è Nilperché non ci sono stati errori.

E sembra che in realtà posso chiamare qualsiasi metodo che desidero Nile tutti tornano silenziosamente Nil, comprese cose come Nil.this-is-a-fake-methode Nil.reverse-entropy(123).

Questa è una caratteristica? In tal caso, qual è la logica?

Risposte:


13

È previsto e documentato, sì. Il titolo di Nil"Assenza di un valore o un fallimento benigno" è menzionato nella documentazione della classe

Qualsiasi chiamata a Nilun metodo inesistente e, di conseguenza, qualsiasi operazione di sottoscrizione, avrà esito positivo e verrà restituita Nil.

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

La sinossi 2 afferma "Qualsiasi chiamata di metodo non definita sui Nilrendimenti Nil, in modo da Nilpropagare giù le catene di chiamate del metodo. Allo stesso modo qualsiasi operazione di sottoscrizione sui Nilrendimenti Nil", quindi l'intento sembra consentire espressioni come $foo.Bar()[0].Baz()senza richiedere controlli per Nilogni passaggio, o speciale "Nil-safe "operatori di chiamata di metodo e sottoscrizione.


4
Infatti. E già molto tempo fa: github.com/rakudo/rakudo/commit/174727377f (dicembre 2013) Vedi anche la speculazione associata: design.raku.org/S02.html#Nil
Elizabeth Mattijsen,

1
@ElizabethMattijsen ah, penso che S02 abbia la risposta. "Qualsiasi chiamata di metodo non definita sui Nilritorni Nil, in modo da Nilpropagare giù le catene di chiamate del metodo." Quindi è previsto che tu possa fare $foo.Bar().Baz().Blah()senza aver bisogno di zero test in ogni fase o di un ?.tipo speciale di operatore (purché tu gestisca correttamente lo zero alla fine). Lo modificherò in, grazie.
Hobbs

1
Un Nilerrore che non si risolve e che semplicemente ne getta di più Nilottiene un uso abbastanza esteso in Obj-C e NS Frameworks dove viene utilizzato in modo simile - consente chiamate concatenate che continuano a passare a zero. Non conosco le esatte penalità prestazionali delle eccezioni e la loro cattura, ma la mia ipotesi è che il Nilconcatenamento possa essere più efficiente, se meno flessibile.
user0721090601

1
(ma questa è un'ipotesi del tutto non informata, quindi sono felice che Lizmat o Jnthn mi dica che mi sbaglio e che devo stare zitto :-))
user0721090601

2
La Nilclasse ha un metodo FALLBACK docs.raku.org/language/typesystem#index-entry-FALLBACK_(method) , che restituisce Nil. Fondamentalmente, poco prima che venga generata un'eccezione perché non è stato possibile trovare un metodo, FALLBACKviene eseguito un controllo e viene chiamato se disponibile.
Elizabeth Mattijsen,

5

Questa domanda (e la risposta di Hobbs) mi ha anche fatto sentire ... a disagio: alla fine ho trovato https://docs.raku.org/language/traps : spiega che l'assegnazione Nilproduce un valore diverso, di solito Any. Anche la seguente interazione REPL di base lo dimostra:

> 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

((la differenza tra =e :=è coperta qui: https://docs.raku.org/language/containers#Binding ))

... quindi l'idea generale è che il "valore assente o fallimento benigno" è molto meno diffuso nel codice normale di quanto io (e forse anche tu?) improvvisamente temuto: succede comunque quando si lavora con regex corrisponde direttamente e nella tua particolare situazione accidentale relativa ai metodi di invocazione diretta su $!.

Questo mi ha reso più consapevole della differenza tra Nile Any, e la logica fornita aveva più senso per me.


2
Nilimposta un contenitore sul suo stato predefinito. È possibile modificare il valore predefinito. my $foo is default(42) = 5; $foo = Nil; say $foo; # 42
Brad Gilbert,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.