Почему некоторые пользователи цитируют имена классов в Perl?

12

Глядя на это Type::Tiny, я вижу, что имя класса в вызове Type::Tiny->newуказано в официальных документах,

my $NUM = "Type::Tiny"->new(
   name       => "Number",
   constraint => sub { looks_like_number($_) },
   message    => sub { "$_ ain't a number" },
);

Почему это? Это простой стиль? Есть ли какие-либо последствия для производительности для этой практики?

Эван Кэрролл
источник

Ответы:

7

Возьмите более простой пример

package Foo { sub new { die 7  } };
package Bar { sub new { die 42 } };
sub Foo { "Bar" }
Foo->new();

В приведенном выше примере константа Fooпреобразуется в «Бар», так что это "Bar"->newне вызывает "Foo"->new. Как вы мешаете разрешению подпрограммы? Вы можете процитировать это.

"Foo"->new();

Что касается влияния на производительность, все не становится хуже, если использовать строку, а не голое слово. Я подтвердил, что сгенерированный optree O=Deparseтакой же. Так что, как правило, кажется, что лучше указывать имена классов, если вы цените правильность.

Это упоминается в программировании на Perl (к сожалению, в контексте косвенного вызова метода )

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

use ElvenRing;
require ElvenRing;

Любое из этих объявлений гарантирует, что Perl знает, ElvenRingчто это имя модуля, что заставляет любое голое имя, как newперед именем класса ElvenRing, интерпретироваться как вызов метода, даже если вам случится объявить собственную newподпрограмму в текущем пакете.

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

tldr; Вероятно, это хорошая идея - указывать свои имена классов, если вы их знаете и цените правильность, а не беспорядок.

Примечание: в качестве альтернативы вы можете остановить преобразование голого слова в функцию, добавив к его концу символ a ::, например, как описано выше Foo::->new.


Спасибо Ginnz на Reddit за указание на это мне , и Тоби Инкстер за комментарий (хотя это не имело смысла для меня при первом прочтении).

Эван Кэрролл
источник
2
Или Foo::->new, как я узнал от икегами.
моб
@mob да, книга Perl упоминает об этом тоже (в явном виде), не уверен, стоит ли мне помещать это туда или нет? лол.
Эван Кэрролл
@mob Я тоже добавил это, как сноску. Хотя я не уверен, что мне это нравится.
Эван Кэрролл
1
Foo::имеет преимущество в том, что он предупреждает, если Fooпакет не существует
ikegami
1
Try :: Tiny - лучший пример, чем Foo. При использовании кода Foo->вы можете ожидать, что в вашем пакете нет такой подпрограммы. Но если вы используете Try :: Tiny и ряд других модулей cpan / других внешних источников, кто скажет, использует ли один из них пакет Try, и если да, есть ли в нем сабвуфер Tiny?
ysth
0

Явное цитирование имени класса вместо использования голого слова (которое рассматривается как строка) является одним из трех способов избежать синтаксической неоднозначности. Методы Вызывающую класса раздел документации perlobj объясняет.

Поскольку Perl позволяет вам использовать голые слова для имен пакетов и подпрограмм, он иногда неправильно интерпретирует значение голого слова. Например, эта конструкция Class->new()может быть интерпретирована как 'Class'->new()или Class()->new(). На английском языке эта вторая интерпретация читается как «вызов подпрограммы с именем Class(), а затем вызов new()метода в качестве возвращаемого значения Class()». Если Class()в текущем пространстве имен есть подпрограмма , Perl всегда будет интерпретировать ее Class->new()как второй вариант: вызов new()объекта, возвращаемого вызовом Class().

Посмотрите этот странный случай в действии с демонстрацией ниже.

#! /usr/bin/env perl

use strict;
use warnings;

sub Type::Tiny { print "Returning Bogus\n" ; return "Bogus" }

sub Type::Tiny::new { print "Type::Tiny::new\n" }

sub Bogus::new { print "Bogus::new\n" }

my $class = "Type::Tiny";

Type::Tiny->new;
Type::Tiny::->new;
"Type::Tiny"->new;
$class->new;

Его вывод

Возвращение Поддельного
Поддельный :: новый
Тип :: Крошечный :: новый
Тип :: Крошечный :: новый
Тип :: Крошечный :: новый

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

Вы можете заставить Perl использовать первую интерпретацию ( т. Е. Как вызов метода для именованного класса "Class") двумя способами. Во-первых, вы можете добавить ::к имени класса:

Class::->new()

Perl всегда будет интерпретировать это как вызов метода.

Кроме того, вы можете указать имя класса:

'Class'->new()

Конечно, если имя класса находится в скаляре, Perl будет делать то же самое:

my $class = 'Class';
$class->new();

Применительно к вашему вопросу все звонки ниже эквивалентны.

Type::Tiny::->new(  );

"Type::Tiny"->new(  );

my $class = "Type::Tiny";
$class->new(  );

Добавление ::до конца имеет то преимущество, что выдает полезное предупреждение. Скажи, что ты случайно набрал

Type::Tinny::->new;

Что производит

Базовое слово "Type :: Tinny ::" относится к несуществующему пакету в ./try line 15.
Не удается найти метод объекта "new" через пакет "Type :: Tinny" (возможно, вы забыли загрузить "Type :: Tinny"?) В ​​строке ./try 15.
Грег Бэкон
источник