hangon has asked for the wisdom of the Perl Monks concerning the following question:

Before this digresses into a tangental discussion - I rarely use prototypes, and yes I do know why I want to use one in this case.

I prefer to place subroutines at the bottom of my programs, which creates a problem with using a prototyped sub. The obvious answer (to me) is to use a BEGIN block, but this doesn't work:

use strict; use warnings; foo('bar'); BEGIN{ sub foo ($){ print "foo called: @_\n"; } } # gives warning message: # main::foo() called too early to check prototype ...

While the following does the job, and I'm ok with it, I'm curious as to why the BEGIN block doesn't work. Can anyone provide some enlightenment?

use strict; use warnings; sub foo ($); foo('bar'); sub foo ($){ print "foo called: @_\n"; }

Replies are listed 'Best First'.
Re: BEGIN block and prototyped subroutines
by JavaFan (Canon) on Apr 16, 2010 at 17:02 UTC
    The begin block doesn't work because it's too late. In fact, the BEGIN block in your code does not serve any purpose (there's no code the run).

    Prototypes come in action at compile time. When foo('bar') is compiled, the BEGIN block hasn't been seen. Nor has the definition of sub foo.

    If you use prototypes, you must make the prototype known before the call to the sub is compiled. Otherwise, the compiler cannot know what to check/how to compile the call.

Re: BEGIN block and prototyped subroutines
by moritz (Cardinal) on Apr 16, 2010 at 17:02 UTC
    While the following does the job, and I'm ok with it, I'm curious as to why the BEGIN block doesn't work. Can anyone provide some enlightenment?

    Because the parses sees the call of foo before the declaration (yes, the "called too early" warning happens at compile time). A BEGIN block further down the file is still parsed later than the lines before it.

    Perl 6 - links to (nearly) everything that is Perl 6.
Re: BEGIN block and prototyped subroutines
by cdarke (Prior) on Apr 17, 2010 at 15:36 UTC
    The BEGIN block is executed at parse time, but only when the parser finds it - it works top-down so it finds the prototype definition after it has parsed the call, when it is too late.

    There is an alternative, which uses a similar technique to C, and that is to declare the prototype before the call. For example:
    use warnings; use strict; mysub('duff'); sub mysub() { print "mysub\n" }
    Gives:
    main::mysub() called too early to check prototype
    But we add the declaration:
    use warnings; use strict; sub mysub(); # mysub declaration mysub('duff'); sub mysub() { print "mysub\n" }
    and we get:
    Too many arguments for main::mysub
    as expected.

      Ok thanks. Thinking about this and a little more research clears up some misconceptions I had about how the parse/compile/run cycle works.

Re: BEGIN block and prototyped subroutines
by ikegami (Patriarch) on Apr 18, 2010 at 08:30 UTC

    You seem to misunderstand both BEGIN and prototypes.

    Let's look at BEGIN first. It causes the enclosed code to be executed as soon as it is compiled.

    For the following code:

    print "a"; BEGIN { print "b"; } print "c";

    the following happens:

    • print "a"; is compiled.
    • BEGIN { ... } is compiled.
      • print "b"; is compiled.
    • BEGIN { ... } is executed.
      • print "b"; is executed.
    • print "c"; is compiled.
    • Compilation finished. Execution begins.
    • print "a"; is executed.
    • print "c"; is executed.

    BEGIN is completely useless around a sub definition because a sub definition doesn't produce any runnable code.

    Now, let's look at prototypes. Prototypes affect how sub calls are parsed and they affect the code into which a sub call is compiled. Obviously, that means the prototype of a sub must be known at the time a call to the sub is encountered.

    For the following code:

    foo('bar'); sub foo($) { print "foo called: @_\n"; }
    the following happens:
    • foo('bar') is compiled. foo hasn't been declared, so a stub with no prototype is created. No prototype exists, so parsing is not affected, and no check is done on the args.
    • sub foo($) { ... } is compiled. The prototype doesn't match the one that was previously declared, which means Perl used the wrong prototype when generating earlier calls to foo, so a warning is issued.
    • Compilation finished. Execution begins.
    • ...

    You need to declare the sub before any calls to the sub are compiled. It's the only way the sub call can be affected by the prototype.

    sob foo($); foo('bar'); sub foo($) { print "foo called: @_\n"; }

    By the way, prototypes are by and large discouraged in Perl. They are not a good way to check the validity of arguments.