Почему прототипы функций Perl 5 плохие? - PullRequest
115 голосов
/ 18 ноября 2008

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

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

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

Ответы [ 4 ]

120 голосов
/ 18 ноября 2008

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

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

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

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

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

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

mypush @array, 1, 2, 3;

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

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

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

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

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

68 голосов
/ 18 ноября 2008

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

30 голосов
/ 18 ноября 2008

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

4 голосов
/ 24 апреля 2016

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

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

...