Hack 70. Understand What Happens When


Tell compile time from runtime.

Dynamic languages are flexible, neither requiring you to know all of the code you're ever going to run in a program at compile time nor necessarily failing if it's not there at runtime. Perl can live with some ambiguity about seeing functions you haven't defined yet (if ever) and referring to variables that don't necessarily have any values yet.

That doesn't always make life easier for programmers. While Perl's pretty good about knowing what happens when, reading the source code doesn't always make it clear. While it may seem obvious to you that program execution happens top to bottom that's not always how it works.

The Hack

Here's what actually happens.

Compilation

When you first run your program, Perl reads the file and starts compiling from top to bottom. At this point, it looks for symbols (variables and subroutines), registers them appropriately, and converts the text of the program into an internal representation that it can execute. If it encounters syntax errors, it aborts and reports an error message.

Of course, some constructs aren't syntax errors in normal use:

#!/usr/bin/perl my $age = 10; print $aeg;

Perl will only complain about an undeclared variable $aeg when running under the strict pragma. However, the question of how this works is less obvious when you consider that Perl reports this error before running the code. Consider:

#!/usr/bin/perl use strict; my $age = 10; print $aeg;

The secret is that use internally becomes:

BEGIN {     require 'strict';     strict->import( ) if strict->can( 'import' ); }

Perl then loads the strict module, if it can, and starts compiling that, returning to the main program when it finishes.

Whenever Perl encounters a BEGIN block, it executes its contents immediately, just as it encounters them. Of course, it doesn't execute code outside of the block that a programmer might think is important to the block:

my $name = 'Spot'; BEGIN { print "Hello, $name!\\n" }

Though the BEGIN block executes as soon as Perl encounters it, and though Perl has already associated $name with the appropriate storage spot inside and outside of the block, the assignment will not happen until runtime; the BEGIN block executes before the assignment happens, even if it comes later in the file.

Even though it may seem correct that this would work if it were part of a module loaded from the main program (at least with use), the internal BEGIN will still execute before the rest of the code in the file.

Initialization

As soon as Perl finishes compiling, it runs any CHECK blocks foundbut in reverse order of their declaration. For example:

#!/usr/bin/perl BEGIN { print "First!\\n"  } CHECK { print "Third!\\n"  } CHECK { print "Second!\\n" }

prints:

First! Second! Third!

INIT blocks run after all CHECK blocks in order of their appearance:

#!/usr/bin/perl BEGIN { print "First!\\n"  } INIT  { print "Fourth!\\n" } CHECK { print "Third!\\n"  } CHECK { print "Second!\\n" } INIT  { print "Fifth!\\n"  }                

prints:

First! Second! Third! Fourth!                   Fifth!                

Runtime

When running, execution order happens as you might expect. There aren't any surprises unless you do something tricky (as per most of the rest of the book). One change is that running code by evaling a stringafter runtime startswill only execute any BEGIN blocks found as a result of that operation, not CHECK or INIT blocks.[1] This program:

[1] This is why Attribute::Handlers and persistent interpreters such as mod_perl do not get along by default.

#!/usr/bin/perl   BEGIN { print "First!\\n"  }   INIT  { print "Fourth!\\n" }   CHECK { print "Third!\\n"  }   CHECK { print "Second!\\n" }   INIT  { print "Fifth!\\n"  }   eval <<END_EVAL;                     BEGIN { print "BEGIN in eval\\n!" }                     CHECK { print "CHECK in eval\\n!" }                     INIT  { print "INIT in eval\\n!"  }                     END_EVAL                

prints:

First! Second! Third! Fourth! Fifth! BEGIN in eval!                

Cleanup

Finally, when it comes time for the program to exit (but not with a compilation error), Perl runs all END blocks in reverse order of their appearance:

#!/usr/bin/perl   BEGIN { print "First!\\n"  }   INIT  { print "Fourth!\\n" }   CHECK { print "Third!\\n"  }   CHECK { print "Second!\\n" }   INIT  { print "Fifth!\\n"  }   eval <<END_EVAL;   BEGIN { print "BEGIN in eval\\n!" }   CHECK { print "CHECK in eval\\n!" }   INIT  { print "INIT in eval\\n!"  }   END   { print "Sixth!\\n"         }                     END_EVAL                     END   { print "Seventh!\\n"       }                     END   { print "Eighth!\\n"        }                

prints:

First! Second! Third! Fourth! Fifth! BEGIN in eval! Sixth!                   Seventh!                   Eighth!                

Why did the END block in the eval execute first? Although it's an END block and Perl encountered the string first, it executes that block at runtime, so it's the final END block compiled and, thus, the first to execute.

See perlmod for more details.



Perl Hacks
Perl Hacks: Tips & Tools for Programming, Debugging, and Surviving
ISBN: 0596526741
EAN: 2147483647
Year: 2004
Pages: 141

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net