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

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

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

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

4 ответов


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

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

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

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

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

и назовите это как

mypush @array, 1, 2, 3;

не нужно писать \ взять ссылку на массив.

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

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

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

посмотреть прототипы в perlsub для всех кровавых деталей.


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

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

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

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

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

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.


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


некоторые люди, глядя на прототип подпрограммы 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";
    }

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