为什么Perl 5的函数原型不好?
在另一个Stack Overflow问题中, Leon Timmermans断言:
我会build议你不要使用原型。 他们有他们的用途,但不是在大多数情况下,绝对不在这一个。
为什么这可能是真的(或以其他方式)? 我几乎总是为我的Perl函数提供原型,而且我从来没有见过其他人说过使用它们的坏处。
如果使用正确,原型也不错。 Perl的原型不能像人们期望的那样工作。 具有其他编程语言背景的人倾向于期望原型提供一种机制来检查函数调用是否正确:也就是说,他们拥有正确的参数数量和types。 Perl的原型不适合这个任务。 这是错误的 ,这是不好的。 Perl的原型有一个非常不同的目的:
原型允许您定义像内置函数一样的函数。
- 括号是可选的。
- 上下文被强加在参数上。
例如,你可以定义一个这样的函数:
sub mypush(\@@) { ... }
并称之为
mypush @array, 1, 2, 3;
而不需要写入\
参考数组。
简而言之,原型可以让您创build自己的语法糖。 例如,Moose框架使用它们来模拟更典型的OO语法。
这是非常有用的,但原型非常有限:
- 它们必须在编译时可见。
- 他们可以被绕过。
- 将上下文传播给参数可能会导致意外的行为。
- 他们可以使用除严格规定的表格之外的任何东西来调用函数是很困难的。
请参阅perlsub中的原型 ,了解所有血腥细节。
问题在于,Perl的函数原型并没有做到人们认为他们做的事情。 他们的目的是让你编写像Perl的内置函数那样被parsing的函数。
首先,方法调用完全忽略原型。 如果你正在做OO编程,你的方法有什么样的原型并不重要。 (所以他们不应该有任何原型。)
其次,原型没有严格执行。 如果使用&function(...)
调用子程序,原型将被忽略。 所以他们不提供任何types的安全。
第三,他们是一个惊人的行动。 (特别是$
原型,这会导致相应的参数在标量上下文中被评估,而不是默认的列表上下文。)
特别是,它们很难从数组中传递参数。 例如:
my @array = qw(abc); 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]);
打印:
abc ab abc 3 b abc
以及关于main::foo() called too early to check prototype
3个警告, main::foo() called too early to check prototype
(如果警告已启用)。 问题是在标量上下文中计算的数组(或数组切片)返回数组的长度。
如果你需要编写一个像内置的函数,使用原型。 否则,不要使用原型。
注意:Perl 6将会有完整的更新和非常有用的原型。 这个答案只适用于Perl 5。
我同意以上两张海报。 一般来说,应该避免使用$
。 原型仅在使用块参数( &
),球体( *
)或参考原型( \@
, \$
, \%
, \*
)
有些人看着一个Perl子程序的原型,认为这意味着它不是:
sub some_sub ($$) { ... }
对Perl来说,这意味着parsing器需要两个参数。 这就是让Perl创build像内置程序一样行为的子程序的方法,所有这些子程序都知道后续代码的期望。 你可以阅读关于perlsub的原型
在不阅读文档的情况下,人们会猜测原型是指运行时参数检查或其他语言中类似的东西。 就像人们猜测Perl的大多数情况一样,事实certificate他们错了。
然而,从Perl v5.20开始,Perl有一个特性,就是我写的这个实验,它提供了更像用户期望的内容和内容。 Perl的子程序签名不会运行时间自variables计数,variables赋值和默认设置:
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"; }
如果您正在考虑原型,这是您可能需要的function。