为什么Perl 5的功能原型很糟糕?

时间:2008-11-17 21:37:58

标签: perl function prototype

another Stack Overflow question Leon Timmermans断言:

  

我建议你不要使用原型。它们有它们的用途,但在大多数情况下都没有,绝对不是在这个中。

为什么这可能是真的(或其他)?我几乎总是为我的Perl函数提供原型,而且我从来没有见过其他任何人说过使用它们的坏事。

4 个答案:

答案 0 :(得分:120)

如果使用正确,原型也不错。困难在于Perl的原型不像人们通常期望的那样工作。具有其他编程语言背景的人倾向于期望原型提供检查函数调用是否正确的机制:即,他们具有正确数量和类型的参数。 Perl的原型并不适合这项任务。这是滥用这是不好的。 Perl的原型有一个非常不同的目的:

原型允许您定义与内置函数类似的函数。

  • 括号是可选的。
  • 对参数施加了上下文。

例如,您可以定义如下函数:

sub mypush(\@@) { ... }

并将其命名为

mypush @array, 1, 2, 3;

无需编写\来获取对数组的引用。

简而言之,原型可让您创建自己的语法糖。例如,Moose框架使用它们来模拟更典型的OO语法。

这非常有用,但原型非常有限:

  • 它们必须在编译时可见。
  • 可以绕过它们。
  • 将参数传播到参数可能会导致意外行为。
  • 他们可能很难使用除了之外的任何东西来调用函数 严格规定的形式。

有关所有血腥的详细信息,请参阅perlsub中的Prototypes

答案 1 :(得分:68)

问题在于Perl的功能原型并不像人们认为的那样做。它们的目的是允许您编写将像Perl的内置函数一样进行解析的函数。

首先,方法调用完全忽略原型。如果您正在进行OO编程,那么您的方法所具有的原型并不重要。 (所以他们不应该有任何原型。)

其次,原型并未严格执行。如果使用&function(...)调用子例程,则会忽略原型。所以他们并没有真正提供任何类型的安全。

第三,他们是远距离的幽灵行动。 (特别是$原型,它导致在标量上下文中计算相应的参数,而不是默认的列表上下文。)

特别是,它们使得从数组传递参数变得困难。例如:

my @array = qw(a b c);

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]);

打印:

a b c
a b
a b c
3
b
a b c

以及有关main::foo() called too early to check prototype的3个警告(如果启用了警告)。问题是在标量上下文中计算的数组(或数组切片)返回数组的长度。

如果您需要编写一个类似于内置函数的函数,请使用原型。否则,不要使用原型。

注意:Perl 6将完全改造并且非常有用的原型。这个答案仅适用于Perl 5.

答案 2 :(得分:30)

我同意上述两张海报。通常,应避免使用$。原型仅在使用块参数(&),globs(*)或参考原型(\@\$\%,{{1 }})

答案 3 :(得分:4)

有些人看着Perl子程序原型,认为这意味着它没有:

sub some_sub ($$) { ... }

对于Perl,这意味着解析器需要两个参数。它是Perl的一种让你创建行为类似于内置函数的子程序的方法,所有这些程序都知道对后续代码的期望。您可以在perlsub

中了解原型

在没有阅读文档的情况下,人们猜测原型是指运行时参数检查或类似于他们在其他语言中看到的类似物。正如人们猜测Perl的大多数事情一样,它们都是错误的。

然而,从Perl v5.20开始,Perl有一个功能,在我写这篇文章时是实验性的,它提供了更像用户期望的内容和内容。 Perl的subroutine signatures执行时间参数计数,变量赋值和默认设置:

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";
    }

如果您正在考虑原型,这可能是您想要的功能。