Почему прототипы функций Perl 5 плохие?
В другом вопросе переполнения стека Leon Timmermans заявил:
Я бы посоветовал вам не использовать прототипы. У них есть свои применения, но не в большинстве случаев и определенно не в этом.
Почему это может быть правдой (или иначе)? Я почти всегда поставляю прототипы для своих функций Perl, и я никогда раньше не видел, чтобы кто-то говорил что-то плохое об их использовании.
4 ответа
Прототипы не плохи, если используются правильно. Сложность в том, что прототипы Perl не работают так, как их ожидают люди. Люди с опытом работы в других языках программирования склонны ожидать, что прототипы предоставят механизм для проверки правильности вызовов функций: то есть, что они имеют правильное число и тип аргументов. Прототипы Perl не подходят для этой задачи. Это неправильное использование, это плохо. Прототипы Perl имеют единственное и совсем другое назначение:
Прототипы позволяют вам определять функции, которые ведут себя как встроенные функции.
- Скобки необязательны.
- Контекст навязывается аргументам.
Например, вы можете определить функцию следующим образом:
sub mypush(\@@) { ... }
и называть это как
mypush @array, 1, 2, 3;
без необходимости писать \
взять ссылку на массив.
Короче говоря, прототипы позволяют вам создавать свой собственный синтаксический сахар. Например, среда Moose использует их для эмуляции более типичного синтаксиса OO.
Это очень полезно, но прототипы очень ограничены:
- Они должны быть видны во время компиляции.
- Их можно обойти.
- Распространение контекста на аргументы может вызвать неожиданное поведение.
- Они могут затруднить вызов функций с использованием чего-либо, кроме строго предписанной формы.
Смотрите Prototypes в perlsub для всех кровавых деталей.
Проблема в том, что прототипы функций Perl не делают то, что думают люди. Их цель - позволить вам писать функции, которые будут анализироваться, как встроенные функции Perl.
Прежде всего, вызовы методов полностью игнорируют прототипы. Если вы занимаетесь ОО-программированием, не имеет значения, какой прототип есть у ваших методов. (Таким образом, у них не должно быть никакого прототипа.)
Во-вторых, прототипы не строго соблюдаются. Если вы вызываете подпрограмму с &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.
Я согласен с вышеуказанными двумя постерами. В общем, используя $
необходимо избегать. Прототипы полезны только при использовании аргументов блока (&
), шары (*
) или эталонные прототипы (\@
, \$
, \%
, \*
)
Некоторые люди, глядя на прототип подпрограммы 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";
}
Эта функция, вероятно, вам нужна, если вы рассматриваете прототипы.