Hi! Please consider following me on twitter: @hanekomu.

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: .

Write a comment | Bookmark and Share

posted at: 11:14 | path: /dev | permalink | 0 comments | 0 trackbacks

Comments are closed for this story.

Trackbacks are closed for this story.