Почему прототипы функций Perl 5 плохие?

112

В еще один вопрос о переполнении Stack   Леон Тиммерманс утверждал:

Я бы посоветовал вам не использовать прототипы. У них есть свое применение, но не для большинства случаев и, безусловно, не в этом.

Почему это может быть правдой (или иначе)? Я почти всегда поставляю прототипы для своих функций Perl, и я никогда раньше не видел, чтобы кто-то еще говорил что-то плохое об их использовании.

  • 0
    Мне тоже любопытно. Единственный раз, когда я не использую их, это когда я звоню с переменным количеством аргументов.
  • 7
    Могу ли я рекомендовать вам прочитать статью «Прототипы Perl считаются вредными» ?
Теги:
function
prototype

4 ответа

113
Лучший ответ

Прототипы неплохие, если они используются правильно. Трудность заключается в том, что прототипы Perl не работают так, как люди часто ожидают их. Люди с опытом работы на других языках программирования склонны ожидать, что прототипы будут обеспечивать механизм проверки правильности вызовов функций, т.е. Они имеют правильное количество и тип аргументов. Прототипы Perl не подходят для этой задачи. Это неправильное использование, что плохо. Прототипы Perl имеют единственную и очень различную цель:

Прототипы позволяют вам определять функции, которые ведут себя как встроенные функции.

  • Скобки необязательны.
  • Контекст наложен на аргументы.

Например, вы можете определить такую ​​функцию:

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

и назовите его

mypush @array, 1, 2, 3;

без необходимости писать \, чтобы получить ссылку на массив.

Вкратце, прототипы позволяют создавать свой собственный синтаксический сахар. Например, структура Moose использует их для эмуляции более типичного синтаксиса OO.

Это очень полезно, но прототипы очень ограничены:

  • Они должны быть видимыми во время компиляции.
  • Их можно обойти.
  • Распространение контекста на аргументы может привести к неожиданному поведению.
  • Они могут затруднить вызов функций, используя что-либо, кроме строго предписанная форма.

См. Prototypes в perlsub для всех деталей gory.

Показать ещё 2 комментария
69

Проблема в том, что прототипы функций Perl не делают то, что люди думают, что они делают. Их цель - позволить вам писать функции, которые будут обрабатываться как встроенные функции Perl.

Прежде всего, вызовы методов полностью игнорируют прототипы. Если вы программируете OO, не имеет значения, какой прототип ваших методов имеет. (Поэтому у них не должно быть прототипов.)

Во-вторых, прототипы строго не соблюдаются. Если вы вызываете подпрограмму с &function(...), прототип игнорируется. Таким образом, они не обеспечивают безопасность любого типа.

В-третьих, это пугающее действие на расстоянии. (Особенно прототип $, который вызывает вычисление соответствующего параметра в скалярном контексте вместо контекста списка по умолчанию.)

В частности, они затрудняют передачу параметров из массивов. Например:

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]);

печатает:

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

вместе с 3 предупреждениями о main::foo() called too early to check prototype (если предупреждения включены). Проблема в том, что массив (или срез массива), оцененный в скалярном контексте, возвращает длину массива.

Если вам нужно написать функцию, которая действует как встроенная, используйте прототип. В противном случае не используйте прототипы.

Примечание. Perl 6 будет полностью обновлен и очень полезен для прототипов. Этот ответ относится только к Perl 5.

  • 0
    Но они по-прежнему обеспечивают полезную проверку того, что ваш вызывающий и подчиненный используют одинаковое количество аргументов, так что с этим не так?
  • 0
    В этом и заключается суть моего вопроса - я никогда не использую старый синтаксис & func, поэтому прототип обеспечивает полезную проверку параметров функции.
Показать ещё 4 комментария
28

Я согласен с этими двумя плакатами. В общем случае следует избегать использования $. Прототипы полезны только при использовании аргументов блока (&), globs (*) или эталонных прототипов (\@, \$, \%, \*)

  • 0
    В общем, возможно, но я бы хотел упомянуть два исключения: во-первых, прототип ($) создает именованный унарный оператор, который может быть полезен (конечно, Perl находит их полезными; я тоже иногда). Во-вторых, при переопределении встроенных модулей (будь то с помощью импорта или с помощью CORE :: GLOBAL: :), вы должны в целом придерживаться любого прототипа, который имел встроенный модуль, даже если он включает $ , или вы можете удивить программиста (себя , даже) со списком контекста, где встроенный в противном случае предоставил бы скалярный контекст.
2

Некоторые люди, глядя на прототип подпрограммы Perl, думают, что это означает что-то, чего нет:

sub some_sub ($$) { ... }

В Perl это означает, что парсер ожидает два аргумента. Это Perl способ позволить вам создавать подпрограммы, которые ведут себя как встроенные, все из которых знают, чего ожидать от следующего кода. Вы можете прочитать про прототипы в perlsub

Без чтения документации люди догадываются, что прототипы ссылаются на проверку аргументов времени выполнения или что-то подобное, что они видели на других языках. Как и большинство вещей, которые люди догадываются о Perl, они оказываются ошибочными.

Однако, начиная с Perl v5.20, Perl имеет функцию, экспериментальную, когда я пишу это, что дает нечто большее, что ожидают пользователи и что. Perl подписи подпрограммы запускает подсчет аргументов времени, назначение переменной и значение по умолчанию:

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";
    }

Это функция, которую вы, вероятно, захотите, если вы рассматриваете прототипы.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню