Mogę wywołać dowolną metodę na zero i to wydaje się złe

14

Spędziłem ostatnio dużo czasu na debugowaniu skryptu, a kiedy w końcu znalazłem problem, przyczyną był kod, który wyglądał tak:

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

Okazało się, że problem był z $!.bar, który powinien być albo $!baralbo $.bar. Rozumiem.

Ale dlaczego to nie umiera ?

Patrząc na to w sposób bardziej szczegółowy, wygląda na to problemu jest to, że staram się wywołać (nieistniejącą) sposób barna $!, która w tym momencie jest Nil, bo nie było żadnych błędów.

I wygląda na to, że mogę wywołać dowolną metodę, którą chcę, Nili wszystkie one po cichu powracają Nil, w tym rzeczy takie jak Nil.this-is-a-fake-methodi Nil.reverse-entropy(123).

Czy to jest funkcja? Jeśli tak, jakie jest uzasadnienie?

jja
źródło

Odpowiedzi:

13

Jest przeznaczony i udokumentowany, tak. Nagłówek Nilbrzmi: „Brak wartości lub łagodna porażka”, a dokumentacja klasy wspomina

Każde wywołanie Nilmetody, która nie istnieje, a co za tym idzie, każda operacja subskrypcji, zakończy się powodzeniem i zwróci Nil.

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

Synopsis 2 stwierdza: „Każde niezdefiniowane wywołanie metody po Nilpowrocie Nil, tak że Nilpropaguje łańcuchy wywołań metody. Podobnie jak każda operacja subskrybowania po Nilpowrocie Nil”, więc intencja wydaje się pozwalać na wyrażenia takie, jak $foo.Bar()[0].Baz()bez konieczności sprawdzania Nilna każdym kroku, lub specjalne „zero-bezpieczne msgstr "operatory wywoływania metod i indeksowania.

Hobbs
źródło
4
W rzeczy samej. I już dawno też: github.com/rakudo/rakudo/commit/174727377f (grudzień 2013) Zobacz także powiązane spekulacje: design.raku.org/S02.html#Nil
Elizabeth Mattijsen
1
@ElizabethMattijsen ah, myślę, że S02 ma odpowiedź. „Każde niezdefiniowane wywołanie metody po Nilpowrocie Nil, dzięki czemu Nilpropaguje łańcuchy wywołań metod”. Jest więc zamierzone, abyś mógł to zrobić $foo.Bar().Baz().Blah()bez potrzeby przeprowadzania zerowych testów na każdym kroku lub specjalnego ?.rodzaju operatora (o ile poprawnie obsługujesz zero na końcu). Przeredaguję to, dziękuję.
hobbs
1
To Nil, co nie powoduje błędu i po prostu zwraca więcej, Niljest dość szeroko stosowane w Obj-C i NS Frameworks, gdzie jest używane w podobny sposób - pozwala na połączenia łańcuchowe, które wciąż przekazują zero. Nie znam dokładnych kar za wyjątki i łapania ich, ale domyślam się, że Niltworzenie łańcuchów może być bardziej wydajne, jeśli mniej elastyczne.
user0721090601
1
(ale to całkowicie niedoinformowane przypuszczenie, więc cieszę się, że lizmat lub jnthn powie mi, że się mylę i że muszę się zamknąć :-))
user0721090601
2
NilKlasa ma FALLBACK docs.raku.org/language/typesystem#index-entry-FALLBACK_(method) metodę, która zwraca Nil. Zasadniczo tuż przed zgłoszeniem wyjątku, ponieważ nie można znaleźć metody, sprawdzane FALLBACKjest wykonanie i wywoływane, jeśli jest dostępne.
Elizabeth Mattijsen
5

To pytanie (i odpowiedź Hobbsa) również sprawiło, że poczułem się ... zaniepokojony: w końcu znalazłem https://docs.raku.org/language/traps : wyjaśnia, że ​​przypisywanie Nilzwykle daje inną wartość Any. Wykazuje to również następująca podstawowa interakcja REPL:

> 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

((różnica między =i :=jest tutaj: https://docs.raku.org/language/containers#Binding ))

... więc ogólna idea jest taka, że ​​„nieobecna wartość lub łagodna awaria” jest znacznie mniej rozpowszechniona w zwykłym kodzie, niż ja (a może i ty?) nagle się obawiałem: dzieje się to jednak, gdy zamierzasz pracować z regex pasuje bezpośrednio w danej sytuacji związanej z przypadkowym metod bezpośrednio odwołujących się na $!.

Uczyniło mnie to bardziej świadomym różnicy między Nili Any, a przedstawione uzasadnienie miało dla mnie więcej sensu.

chromis
źródło
2
Nilustawia kontener do stanu domyślnego. Możesz zmienić wartość domyślną. my $foo is default(42) = 5; $foo = Nil; say $foo; # 42
Brad Gilbert