Hi! Please consider following me on twitter: @hanekomu.
2008年09月24日
Dissecting the Moose Part 2 - Pragmatic Goodness
The synopsis of Moose.pm has this innocent-looking line:
use Moose; # automatically turns on strict and warnings
Which begs the question: "How does that work?" In this blog post, I'll try to uncover some of the magic of Perl pragmata.
When you say:
use Moose;
according to perldoc -f use, that really means:
BEGIN { require Moose; Moose->import(LIST); }
But Moose does not have either an import() or an
unimport() subroutine. What's going on?
When loaded, that is, during the require() call, Moose.pm runs
this code:
Moose::Exporter->setup_import_methods(...)
Looking at the setup_import_methods() subroutine in
Moose::Exporter, it goes a few levels deeper but eventually comes up with
anonymous subroutines which it installs in the caller's namespace, that is, in
the Moose namespace, as import() and
unimport().
So now there is an import() subroutine that the
use() code shown above can run.
This still doesn't explain why the strict and
warnings pragmata are turned on for every package that says
use Moose.
If we look at the import() subroutine, we find these two
lines:
strict->import; warnings->import;
Both pragmata, strict and warnings, have
import() subroutines that effectively set a few bits in the magic
variable $^H.
perldoc perlvar has a section on $^H that explains:
This variable contains compile-time hints for the Perl
interpreter. At the end of compilation of a BLOCK the value of this variable is
restored to the value when the interpreter started to compile the BLOCK.
When perl begins to parse any block construct that provides a lexical scope
(e.g., eval body, required file, subroutine body, loop body, or conditional
block), the existing value of $^H is saved, but its value is left unchanged.
When the compilation of the block is completed, it regains the saved value.
Between the points where its value is saved and restored, code that executes
within BEGIN blocks is free to change the value of $^H.
Moose::Exporter explains that calling strict->import;
warnings->import; in a module's import() subroutine works
because these pragmata set $^H, which affects the current
compilation, that is, the file that uses Moose.pm. Therefore Moose doesn't need
to do anything special to make it affect the file that uses Moose rather than
Moose itself, because Moose has already been compiled.
So now we've gotten to the core of things. Let's try our new knowledge in a simple test setup. Let's write a little test program:
#!/usr/bin/env perl use Foo; $x = 1;
Note that we don't turn on strict or warnings, and
we don't declare the variable $x.
Foo.pm looks like this:
package Foo; print "Loading Foo.pm\n"; 1;
When we run the test program, it prints Foo's message, then exits without error.
It doesn't make any difference if we just use the pragmata within Foo.pm:
package Foo; use strict; use warnings; print "Loading Foo.pm\n"; 1;
Running the program of course still doesn't complain that $x is
undeclared.
Now let's do what Moose does and have an import() subroutine
that causes the pragmata to be turned on:
package Foo; use strict; use warnings; sub import { strict->import; warnings->import; } print "Loading Foo.pm\n"; 1;
Now we get the desired effect:
$ perl test.pl Loading Foo.pm Global symbol "$x" requires explicit package name at test.pl line 5. Execution of test.pl aborted due to compilation errors.
Tags: Moose.
posted at: 11:14 | path: /dev | permalink | 0 comments | 0 trackbacks
Comments are closed for this story.
Trackbacks are closed for this story.