Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is parenthesis optional only after sub declaration?

Tags:

perl

(Assume use strict; use warnings; throughout this question.)

I am exploring the usage of sub.

sub bb { print @_; } bb 'a'; 

This works as expected. The parenthesis is optional, like with many other functions, like print, open etc.

However, this causes a compilation error:

bb 'a'; sub bb { print @_; }  String found where operator expected at t13.pl line 4, near "bb 'a'"         (Do you need to predeclare bb?) syntax error at t13.pl line 4, near "bb 'a'" Execution of t13.pl aborted due to compilation errors. 

But this does not:

bb('a'); sub bb { print @_; } 

Similarly, a sub without args, such as:

special_print; my special_print { print $some_stuff } 

Will cause this error:

Bareword "special_print" not allowed while "strict subs" in use at t13.pl line 6. Execution of t13.pl aborted due to compilation errors. 

Ways to alleviate this particular error is:

  • Put & before the sub name, e.g. &special_print
  • Put empty parenthesis after sub name, e.g. special_print()
  • Predeclare special_print with sub special_print at the top of the script.
  • Call special_print after the sub declaration.

My question is, why this special treatment? If I can use a sub globally within the script, why can't I use it any way I want it? Is there a logic to sub being implemented this way?

ETA: I know how I can fix it. I want to know the logic behind this.

like image 838
TLP Avatar asked May 13 '11 13:05

TLP


1 Answers

I think what you are missing is that Perl uses a strictly one-pass parser. It does not scan the file for subroutines, and then go back and compile the rest. Knowing this, the following describes how the one pass parse system works:

In Perl, the sub NAME syntax for declaring a subroutine is equivalent to the following:

sub name {...}   ===   BEGIN {*name = sub {...}} 

This means that the sub NAME syntax has a compile time effect. When Perl is parsing source code, it is working with a current set of declarations. By default, the set is the builtin functions. Since Perl already knows about these, it lets you omit the parenthesis.

As soon as the compiler hits a BEGIN block, it compiles the inside of the block using the current rule set, and then immediately executes the block. If anything in that block changes the rule set (such as adding a subroutine to the current namespace), those new rules will be in effect for the remainder of the parse.

Without a predeclared rule, an identifier will be interpreted as follows:

bareword       ===   'bareword'   # a string bareword LIST  ===   syntax error, missing ',' bareword()     ===   &bareword()  # runtime execution of &bareword &bareword      ===   &bareword    # same &bareword()    ===   &bareword()  # same 

When using strict and warnings as you have stated, barewords will not be converted into strings, so the first example is a syntax error.

When predeclared with any of the following:

sub bareword; use subs 'bareword'; sub bareword {...} BEGIN {*bareword = sub {...}} 

Then the identifier will be interpreted as follows:

bareword      ===   &bareword()     # compile time binding to &bareword bareword LIST ===   &bareword(LIST) # same bareword()    ===   &bareword()     # same &bareword     ===   &bareword       # same &bareword()   ===   &bareword()     # same 

So in order for the first example to not be a syntax error, one of the preceding subroutine declarations must be seen first.

As to the why behind all of this, Perl has a lot of legacy. One of the goals in developing Perl was complete backwards compatibility. A script that works in Perl 1 still works in Perl 5. Because of this, it is not possible to change the rules surrounding bareword parsing.

That said, you will be hard pressed to find a language that is more flexible in the ways it lets you call subroutines. This allows you to find the method that works best for you. In my own code, if I need to call a subroutine before it has been declared, I usually use name(...), but if that subroutine has a prototype, I will call it as &name(...) (and you will get a warning "subroutine called too early to check prototype" if you don't call it this way).

like image 174
Eric Strom Avatar answered Sep 19 '22 08:09

Eric Strom