12

查看Type::Tiny,我看到调用中的类名在Type::Tiny->new官方文档中被引用,

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

为什么是这样?这仅仅是风格吗?这种做法是否有任何性能影响?

4

2 回答 2

8

举一个更简单的例子

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

在上面的示例中,常量Foo解析为“Bar”,因此调用"Bar"->newnot "Foo"->new。你如何阻止子程序解析?你可以引用它。

"Foo"->new();

至于性能影响,使用字符串而不是裸词不会使事情变得更糟。我已经确认生成的 optreeO=Deparse是相同的。因此,作为一般规则,如果您重视正确性,似乎最好引用类名。

这在 Programming Perl 中提到,(遗憾的是在间接方法调用的上下文中)

...所以我们会告诉你,只要有两件事是真的,你几乎总是可以用一个简单的类名逃脱。首先,没有与类同名的子程序。(如果您遵循子例程名称(如以new小写开头)和类名(如以ElvenRing大写开头)的约定,这绝不是问题)。二、类被加载了一个

use ElvenRing;
require ElvenRing;

这些声明中的任何一个都确保 Perl 知道ElvenRing是一个模块名,这会强制new将类名之前的任何裸名ElvenRing解释为方法调用,即使您碰巧new在当前包中声明了自己的子例程。

而且,这是有道理的:只有当您的子例程(通常是小写字母)与类(通常是大写字母)具有相同的名称时,才会发生这种混淆。仅当您违反上述命名约定时,才会发生这种情况。

tldr; 如果你知道它们并且你重视正确性而不是混乱,那么引用你的类名可能是一个好主意。

旁注:或者,您可以通过在函数末尾附加 a::来停止将裸词解析为函数,例如在上面的Foo::->new.


感谢reddit 上的 Ginnz 向我指出这一点,并感谢Toby Inkster 的评论(尽管在第一次阅读时对我来说没有意义)。

于 2019-11-26T02:09:20.753 回答
1

明确引用类名而不是使用裸词(被视为字符串)是避免语法歧义的三种方法之一。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")。首先,您可以将 a 附加::到类名:

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 第 15 行不存在的包。
无法在 ./try 第 15 行通过包“Type::Tinny”(也许您忘记加载“Type::Tinny”?)找到对象方法“new”。
于 2019-11-27T22:54:27.713 回答