Я думаю, что вам не хватает того, что Perl использует строго однопроходный парсер. Он не сканирует файл на наличие подпрограмм, а затем возвращается и компилирует все остальное. Зная это, ниже описано, как работает система однопроходного разбора:
В Perl синтаксис sub NAME
для объявления подпрограммы эквивалентен следующему:
sub name {...} === BEGIN {*name = sub {...}}
Это означает, что синтаксис sub NAME
имеет эффект времени компиляции. Когда Perl анализирует исходный код, он работает с текущим набором объявлений. По умолчанию набор встроенных функций. Поскольку Perl уже знает об этом, он позволяет опустить круглые скобки.
Как только компилятор нажимает на блок BEGIN, он компилирует внутреннюю часть блока, используя текущий набор правил, а затем немедленно выполняет блок. Если что-либо в этом блоке изменит набор правил (например, добавление подпрограммы в текущее пространство имен), эти новые правила будут действовать в течение оставшейся части разбора.
Без предварительно объявленного правила идентификатор будет интерпретироваться следующим образом:
bareword === 'bareword' # a string
bareword LIST === syntax error, missing ','
bareword() === &bareword() # runtime execution of &bareword
&bareword === &bareword # same
&bareword() === &bareword() # same
При использовании строгих правил и предупреждений, как вы указали, голые слова не будут преобразованы в строки, поэтому первый пример - это синтаксическая ошибка.
Когда предварительно объявлено любое из следующего:
sub bareword;
use subs 'bareword';
sub bareword {...}
BEGIN {*bareword = sub {...}}
Тогда идентификатор будет интерпретироваться следующим образом:
bareword === &bareword() # compile time binding to &bareword
bareword LIST === &bareword(LIST) # same
bareword() === &bareword() # same
&bareword === &bareword # same
&bareword() === &bareword() # same
Таким образом, чтобы первый пример не был синтаксической ошибкой, сначала должно быть видно одно из предшествующих объявлений подпрограммы.
Что касается того, что стоит за всем этим, у Perl много наследства. Одной из целей разработки Perl была полная обратная совместимость. Скрипт, который работает в Perl 1, все еще работает в Perl 5. Из-за этого невозможно изменить правила, связанные с анализом голых слов.
Тем не менее, вам будет сложно найти язык, который будет более гибким в способах вызова подпрограмм. Это позволяет вам найти метод, который лучше всего подходит для вас. В моем собственном коде, если мне нужно вызвать подпрограмму до того, как она была объявлена, я обычно использую name(...)
, но если у этой подпрограммы есть прототип, я назову ее как &name(...)
(и вы получите предупреждение «Подпрограмма» Вызов слишком рано, чтобы проверить прототип ", если вы не называете это так).