Dlaczego prototypy funkcji w Perlu 5 są złe?

116

W innym pytaniu o przepełnienie stosu Leon Timmermans zapewnił:

Radziłbym nie używać prototypów. Mają swoje zastosowania, ale nie w większości przypadków i na pewno nie w tym.

Dlaczego to może być prawda (lub inaczej)? Prawie zawsze dostarczam prototypy moich funkcji Perla i nigdy wcześniej nie widziałem nikogo, kto powiedziałby coś złego na temat ich używania.

Alnitak
źródło
Ja też jestem ciekawa. Nie używam ich tylko wtedy, gdy dzwonię ze zmienną liczbą argumentów.
Paul Tomblin
7
Czy mogę polecić przeczytanie artykułu „Prototypy Perla uważane za szkodliwe” ?
tchrist

Odpowiedzi:

121

Prototypy nie są złe, jeśli są prawidłowo używane. Trudność polega na tym, że prototypy Perla nie działają tak, jak ludzie często tego oczekują. Osoby z doświadczeniem w innych językach programowania zwykle oczekują, że prototypy zapewnią mechanizm sprawdzania poprawności wywołań funkcji: to znaczy, że mają odpowiednią liczbę i typ argumentów. Prototypy Perla nie nadają się do tego zadania. To nadużycie jest złe. Prototypy Perla mają jeden i bardzo różny cel:

Prototypy pozwalają zdefiniować funkcje, które zachowują się jak funkcje wbudowane.

  • Nawiasy są opcjonalne.
  • Na argumenty narzuca się kontekst.

Na przykład możesz zdefiniować taką funkcję:

sub mypush(\@@) { ... }

i nazwij to jako

mypush @array, 1, 2, 3;

bez konieczności pisania, \aby pobrać odwołanie do tablicy.

Krótko mówiąc, prototypy pozwalają stworzyć własny cukier syntaktyczny. Na przykład framework Moose używa ich do emulacji bardziej typowej składni OO.

Jest to bardzo przydatne, ale prototypy są bardzo ograniczone:

  • Muszą być widoczne w czasie kompilacji.
  • Można je ominąć.
  • Przeniesienie kontekstu do argumentów może spowodować nieoczekiwane zachowanie.
  • Mogą utrudniać wywoływanie funkcji przy użyciu czegoś innego niż ściśle określona forma.

Zobacz Prototypy w perlsubie, aby poznać wszystkie krwawe szczegóły.

Michael Carman
źródło
2
Przyjąłem tę odpowiedź, ponieważ uważam, że najlepiej odpowiada na pytanie - prototypy nie są wewnętrznie złe, tak po prostu ich używasz.
Alnitak
2
Z drugiej strony,
Kent Fredric
Więc są one błędne?
Peter Mortensen
69

Problem w tym, że prototypy funkcji Perla nie robią tego, co ludzie myślą, że robią. Ich celem jest umożliwienie pisania funkcji, które będą analizowane jak funkcje wbudowane Perla.

Przede wszystkim wywołania metod całkowicie ignorują prototypy. Jeśli robisz programowanie obiektowe, nie ma znaczenia, jaki prototyp mają Twoje metody. (Więc nie powinni mieć żadnego prototypu.)

Po drugie, prototypy nie są ściśle egzekwowane. Jeśli wywołasz podprogram za pomocą &function(...), prototyp jest ignorowany. Więc tak naprawdę nie zapewniają żadnego bezpieczeństwa typu.

Po trzecie, są upiorną akcją na odległość. (Szczególnie $prototyp, który powoduje, że odpowiedni parametr jest oceniany w kontekście skalarnym, zamiast domyślnego kontekstu listy).

W szczególności utrudniają przekazywanie parametrów z tablic. Na przykład:

my @array = qw(a b c);

foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);

sub foo ($;$$) { print "@_\n" }

foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);

wydruki:

a b c
a b
a b c
3
b
a b c

wraz z 3 ostrzeżeniami o main::foo() called too early to check prototype(jeśli ostrzeżenia są włączone). Problem polega na tym, że tablica (lub wycinek tablicy) obliczona w kontekście skalarnym zwraca długość tablicy.

Jeśli potrzebujesz napisać funkcję działającą jak wbudowana, użyj prototypu. W przeciwnym razie nie używaj prototypów.

Uwaga: Perl 6 będzie miał całkowicie odnowione i bardzo przydatne prototypy. Ta odpowiedź dotyczy tylko Perla 5.

cjm
źródło
Ale nadal zapewniają przydatne sprawdzenie, czy rozmówca i subskrybent używają tej samej liczby argumentów, więc co w tym złego?
Paul Tomblin
2
Nie; ogólny konsensus jest taki, że prototypy funkcji Perla zasadniczo nie przynoszą żadnych korzyści. Równie dobrze możesz się nimi nie przejmować, przynajmniej w Perlu 5. Perl 6 może być inną (lepszą) historią.
Jonathan Leffler
5
Istnieją lepsze sposoby sprawdzania poprawności argumentów, takie jak moduł Params :: Validate: search.cpan.org/~drolsky/Params-Validate-0.91/lib/Params/ ...
friedo
10
Poprawka: dzielenie tablic zwraca listę , więc wycinek tablicy w kontekście skalarnym zwraca ostatni element listy. Twoje przedostatnie wywołanie foo()print 2, ponieważ jest to ostatni element w Twoim wycinku z dwoma elementami. Zmień na, my @array = qw(foo bar baz)a zobaczysz różnicę. (Tak na marginesie, dlatego nie inicjuję tablic / list w sekwencjach liczbowych opartych na 0 lub 1 w wyrzuconym, demonstracyjnym kodzie. Pomieszanie indeksów, liczników i elementów w kontekstach ugryzło mnie więcej niż raz. Głupie, ale prawdziwe.)
pilcrow
2
@pilcrow: Zmieniłem odpowiedź, aby wyjaśnić a b cTwój punkt widzenia.
Flimm
30

Zgadzam się z powyższymi dwoma plakatami. Generalnie $należy unikać używania . Prototypy są użyteczne tylko przy użyciu argumentów bloku ( &) globs ( *) lub prototypy odniesienia ( \@, \$, \%, \*)

Leon Timmermans
źródło
Ogólnie być może, ale chciałbym wspomnieć o dwóch wyjątkach: Po pierwsze, ($)prototyp tworzy nazwany operator jednoargumentowy, który może być przydatny (z pewnością Perl uzna je za przydatne; ja też czasami). Po drugie, przy nadpisywaniu wbudowanych (czy to przez import, czy przy użyciu CORE :: GLOBAL: :), powinieneś ogólnie trzymać się dowolnego prototypu, który miał wbudowany, nawet jeśli zawiera on a $, lub możesz zaskoczyć programistę (siebie, parzysty) z kontekstem listowym, gdzie w przeciwnym razie funkcja wbudowana zapewniłaby kontekst skalarny.
Sidhekin
4

Niektórzy ludzie, patrząc na prototyp podprogramu Perl, myślą, że oznacza coś, czego nie robi:

sub some_sub ($$) { ... }

Dla Perla oznacza to, że parser oczekuje dwóch argumentów. W ten sposób Perl pozwala ci tworzyć podprogramy zachowujące się jak wbudowane, z których wszystkie wiedzą, czego się spodziewać po kolejnym kodzie. Możesz przeczytać o prototypach w perlsub

Bez czytania dokumentacji ludzie domyślają się, że prototypy odnoszą się do sprawdzania argumentów w czasie wykonywania lub czegoś podobnego, co widzieli w innych językach. Jak w przypadku większości rzeczy, które ludzie domyślają się o Perlu, okazują się one błędne.

Jednak począwszy od Perla v5.20, Perl ma funkcję, eksperymentalną w chwili, gdy to piszę, która daje coś bardziej podobnego do tego, czego oczekują użytkownicy i czego. PerlaPodprogramy zliczają argumenty w czasie wykonywania, przypisują zmienne i ustawiają domyślne:

use v5.20;
use feature qw(signatures);
no warnings qw(experimental::signatures);

animals( 'Buster', 'Nikki', 'Godzilla' );

sub animals ($cat, $dog, $lizard = 'Default reptile') { 
    say "The cat is $cat";
    say "The dog is $dog";
    say "The lizard is $lizard";
    }

Jest to funkcja, której prawdopodobnie potrzebujesz, jeśli rozważasz prototypy.

brian d foy
źródło