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


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

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

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

4 112

4 ответа:

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

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

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

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

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

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

mypush @array, 1, 2, 3;

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

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

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

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

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

проблема в том, что прототипы функций 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.

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

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

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