directory/.pm 曾经是一个约定吗?它为啥存在?

Posted

技术标签:

【中文标题】directory/.pm 曾经是一个约定吗?它为啥存在?【英文标题】:Was directory/.pm ever a convention, and why does it exist?directory/.pm 曾经是一个约定吗?它为什么存在? 【发布时间】:2020-03-25 05:17:13 【问题描述】:

Having just figured out this,我想知道-M 将如何处理附加到包名称的终端::

$ perl -MFoo:: -e1

我得到的是……

@INC 中找不到Foo/.pm(您可能需要安装Foo:: 模块)(@INC 包含:[...])。

这似乎表明它不起作用,所以好奇心迫使我......

$ mkdir Foo;
$ echo "package Foo   sub k  die 42   ; 1;" > Foo/.pm
$ PERL5LIB=. perl -MFoo:: -e'Foo::->k'

这确实有效。是否支持 use mod::; 解析为 mod/.pm 的约定?这是从古代遗留下来的。这种行为是从哪里来的?

有了这个架构我可以做到,

Foo/.pm         # package Foo
Foo/Bar/.pm     # package Foo::Bar
Foo/Bar/Baz/.pm # package Foo::Bar::Baz;

那我就可以了

use Foo::;            # resolves to Foo/.pm
use Foo::Bar::;       # resolves to Foo/Bar/.pm
use Foo::Bar::Baz::;  # resolves to Foo/Bar/Baz/.pm

Foo::->new;
Foo::Bar::->new;
Foo::Bar::Baz::->new;

我喜欢这个,因为这增加了

的好处
    让我的所有课程都变成空话。 在use 和调用之间添加一致性。

这个设计是有意的吗?它在任何地方都有记录吗?以前有没有人想过这个很棒的主意?

【问题讨论】:

我想念这比不使用尾随 :: 使您的课程裸词和 use 和调用之间的一致性更好。而且我希望如果 P5P 觉得这令人困惑并去除了尾部斜线,他们会毫不犹豫地删除它,尽管我怀疑是否会有任何动力对此采取任何行动。 我在顶部有一个链接。本质上,如果您有一个名为Foo::Bar 的包并且您使用Foo::Bar->new 调用,那么如果您有一个package Foo sub Bar "Baz" ,您实际上是在执行( Foo::Bar() )->new,即"Baz"->new。所以你可以做Foo::Bar::->new 来解决这个问题,但是你的use 导入是不同的。有趣的是,您也可以安装 use Foo::Bar:: 来工作。那么你的use 和你对包的引用是一样的。 use Foo::Bar::; Foo::Bar::->new 【参考方案1】:

这是使用 mod::; 的约定吗?解析到 mod/.pm 支持?

没有。

解析器在接受::时过于宽松,让你做一些相当荒谬的事情。

$ cat Foo/Bar.pm
package Foo::Bar;
sub import  print "imported.\n"; 
print "loaded.\n";
1

$ perl -I. -e'require Foo::Bar'
loaded.

$ perl -I. -e'require Foo::::::Bar'
loaded.

使用非规范形式会导致问题。

$ perl -I. -e'use Foo::Bar; use Foo::Bar; use Foo::Bar;'
loaded.
imported.
imported.
imported.

$ perl -I. -e'use Foo::::::Bar; use Foo::::::Bar; use Foo::Bar;'
loaded.
loaded.
imported.

同样,use Foo::Bar:: 加载 Foo/Bar/.pm 不应被视为有意支持的功能。

以前有没有人想过这个很棒的主意?

实际上会一团糟。

对于初学者,您必须使用use Foo::Bar::; 而不是use Foo::Bar;

$ cat Foo/Bar/.pm
package Foo::Bar;
sub import  print "imported.\n"; 
sub baz  print "ok.\n"; 
1

$ perl -I. -e'use Foo::Bar;'
Can't locate Foo/Bar.pm in @INC ...

$ perl -I. -e'use Foo::Bar::;'

$

但是使用use Foo::Bar::; 是不够的,因为从未调用过import。 package 指令需要与要调用的 import 的文件名 (package Foo::Bar::) 匹配,这意味着您还必须调整对包的限定引用。

$ cat Foo/Bar/.pm
package Foo::Bar::;
sub import  print "imported.\n"; 
sub baz  print "ok.\n"; 
1

$ perl -I. -e'use strict; use Foo::Bar; Foo::Bar::baz();'
Can't locate Foo/Bar.pm in @INC ...
BEGIN failed--compilation aborted at -e line 1.

$ perl -I. -e'use strict; use Foo::Bar::; Foo::Bar::baz();'
imported.
Undefined subroutine &Foo::Bar::baz called at -e line 1.

$ perl -I. -e'use strict; use Foo::Bar::; Foo::Bar::::baz();'
imported.
ok.

说白了,这是不可取的。

即使您的模块此时没有 import 方法,也可能会发生变化,并且加载模块时使用两种不同且不兼容的约定是有害的。


这种行为从何而来

虽然表达式 Foo::Bar:: 生成字符串 Foo::Bar,但 use 不采用表达式。

此外,文字被传递给包名到文件名音译的实现,不执行完整性检查。 Perl 简单地执行一个相当于调用以下 sub 的操作:

 sub pkg_to_qfn  ( $_[0] =~ s::/gr ) . '.pm' 

【讨论】:

【参考方案2】:

这个约定的一个小缺点是它打破了之前的假设,即Foo/Bar.pmFoo/Bar.pm 中。所以没有终端 ::use 语句不起作用,

mkdir Foo
mkdir Foo/Bar
echo "package Foo::Bar  sub baz    1" > Foo/Bar/.pm

# Dies can't resolve to file,
PERL5LIB=. perl -MFoo::Bar -e1
Can't locate Foo/Bar.pm in @INC ...

# Works fine.
PERL5LIB=. perl -MFoo::Bar:: -e1

所以Foo::Bar::->new 语法与已经工作的use 代码兼容。虽然更改模块的位置会破坏与旧的 use 语句的兼容性,(因此它们也必须更新)。

【讨论】:

使用Foo/Bar/.pm不仅会“破坏”与use Foo::Bar;的兼容性,还会阻止调用模块的导入方法。

以上是关于directory/.pm 曾经是一个约定吗?它为啥存在?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 有命名约定吗?

这个算法有名字吗? (我一直称它为changeBinary)

Swift:XCTest 会修改状态吗?可以返回可选值的测试函数的约定是啥?

Gradle 可以处理不符合默认约定的构建目录结构吗?

使用 mvvmcross,我如何“共享”一个视图模型以使用 iphone 的视图和 ipad 的另一个视图?有啥特殊的命名约定吗?

在哪里可以了解 VS 调试器的“魔术名称”