Perl subs不是词汇范围的设计缺陷吗?

时间:2011-09-23 02:49:58

标签: perl scope perl6 raku

{
  sub a {
    print 1;
  }
}
a;

一个错误,是吗?

a不应该在外面提供。

它在Perl 6 *中有效吗?

*抱歉,我还没有安装它。

7 个答案:

答案 0 :(得分:30)

你问为什么sub在块外是可见的?如果是,那么因为编译时sub关键字将sub放在main命名空间中(除非使用package关键字创建新的命名空间)。你可以试试像

这样的东西
{
  my $a = sub {
    print 1;
  };
  $a->(); # works
}
$a->(); # fails

在这种情况下,sub关键字不是创建子并将其放在main命名空间中,而是创建一个匿名子例程并将其存储在词法范围的变量中。当变量超出范围时,它将不再可用(通常)。

要了解详情,请查看perldoc perlsub

另外,您是否知道可以检查Perl解析器查看代码的方式?使用-MO=Deparse中的标记perl -MO=Deparse yourscript.pl运行perl。您的原始代码解析为:

sub a {
    print 1;
}
{;};
a ;

首先编译sub,然后运行一个没有代码的块,然后调用a

对于我在Perl 6中的示例,请参阅:SuccessFailure。请注意,在Perl 6中,取消引用是.而不是->

编辑:我已添加another answer有关Perl 5.18预期的词汇子程序的新实验支持。

答案 1 :(得分:17)

在Perl 6中,subs确实是词法范围的,这就是代码抛出错误的原因(正如几个人已经指出的那样)。

这有几个有趣的含义:

  • 嵌套的命名子工作作为正确的闭包(另请参阅:perl 5中的“不会保持共享”警告)
  • 从模块中导入subs工作到词法范围
  • 内置函数在程序周围的外部词法范围(“设置”)中提供,因此覆盖就像声明或导入同名函数一样简单
  • 因为lexpads在运行时是不可变的,所以编译器可以在编译时检测对未知例程的调用(niecza已经这样做了,Rakudo仅在“optimizer”分支中)。

答案 2 :(得分:14)

子例程是包作用域,而不是块作用域。

#!/usr/bin/perl
use strict;
use warnings;

package A;
sub a {
    print 1, "\n";
}
a();
1;

package B;
sub a {
    print 2, "\n";
}
a();
1;

答案 3 :(得分:11)

Perl中的命名子例程被创建为全局名称。其他答案已经展示了如何通过为词法变量分配匿名子来创建词法子例程。另一种选择是使用local变量来创建动态范围的子。

两者之间的主要区别在于通话风格和可见性。动态范围的子类可以像命名子一样被调用,并且它也将是全局可见的,直到它被定义的块为止。

use strict;
use warnings;
sub test_sub {
    print "in test_sub\n";
    temp_sub();
}

{
    local *temp_sub = sub {
        print "in temp_sub\n";
    };
    temp_sub();
    test_sub();
}
test_sub();

这应该打印

in temp_sub
in test_sub
in temp_sub
in test_sub
Undefined subroutine &main::temp_sub called at ...

答案 4 :(得分:7)

如果您看到代码编译,运行并打印“1”,那么您没有遇到错误。

您似乎期望子例程只能在定义它们的词法范围内调用。那将是不好的,因为这意味着将无法调用其他文件中定义的子例程。也许你没有意识到每个文件都是在自己的词法范围内进行评估的?这允许喜欢

my $x = ...;
sub f { $x }

答案 5 :(得分:7)

冒着被@tchrist再次责骂的风险,我正在为完整性添加另一个答案。尚未发布的Perl 5.18预计将包含词法子程序作为实验性功能。

Here is a link to the relevant documentation.同样,这是非常实验性的,它不应该用于生产代码,原因有两个:

  1. 可能还没有很好地实现
  2. 可能会在不另行通知的情况下删除
  3. 如果你愿意,可以玩这款新玩具,但是你已经被警告了!

答案 6 :(得分:5)

是的,我认为这是一个设计缺陷 - 更具体地说,最初选择使用动态范围而不是Perl中的词法范围,这自然会导致这种行为。但并非所有语言设计师和用户都同意。所以你问的问题没有明确的答案。

在Perl 5中添加了词法作用域,但作为一项可选功能,您需要特别指出它。有了这个设计选择,我完全同意:向后兼容性非常重要。