-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 =pod Remember when you were in elementary school and your mom used to tell you to look both ways before crossing the street and to always check the return values of functions that don't throw exceptions? Well, the other day I spent an hour or so familiarizing myself with an interesting perl feature courtesy of a module author not doing that. Hopefully he is more careful around traffic. Anyway, here is the scenario. I wrote a module like this: lang:Perl package My::Glue; use Moose; # remember, this turns on strict has 'id' => ( is => 'ro', isa => 'Int', required => 1 ); has 'some_other_class' => ( is => 'ro', isa => 'Some::Other::Class', required => 1, handles => ['foo'], ); # the calling package wants $me->id and $me->get_id. whatever. sub get_id { $self->id } For those following along at home, we have four methods, C, C, C, and C. Simple. That class is loaded by a CPAN module that looks a bit like this: package Glue::Loader; use strict; sub load { my $self = shift; my $module = $self->glue_module; eval "require $module"; my $glue = $module->new( ... this is all fine ... ); $glue->foo; return $glue->get_id; } Great. Once C is setup such that C is the first class we defined above, we call C. Try and guess what the output is. There is none. Instead, an exception is thrown that reads C. What? It's right there in the C. Okay, so maybe I am using the C syntax wrong. Let's just delegate manually: sub foo { my $self = shift; return $self->some_other_class->foo; } sub get_id ... Now we rerun C and everything is fine. Oh wait; no. If everything went fine you wouldn't be reading a blog post about this. We actually get another exception, C. What? So now we are calling C just fine, but C doesn't work? Well whatever. Maybe I have the C version of Moose installed and it's broken? Let's just comment out the delegation and add a C to see what's going on. OK, time to run the program again. (BTW, aren't you glad you are using tests and aren't debugging this in your web browser. I am.) The result? It prints "IT IS WORKING". YAY! IT IS WORKING! Oh... but then it dies on the next line with C. What? So the C declared directly above the C works fine, but C doesn't work? WHAT THE FUCK. I AM SWITCHING TO RUBY!11 All in all, this is one strange debugging session. I can see the code right there! Why doesn't perl see it? Well, it is an exciting combination of a number of features. The biggest problem becomes obvious when we remove the working code and just look at the stuff that's broken: use strict; sub get_id { $self->id } Does the phrase C ring a bell? It should, because you can't just make up variable names and expect your code to compile under strict. So that's one problem. But normally when you forget C, perl just dies and your program never gets past the C or C statement. In this case, though, the author of C forgot to check for errors after his C statement. Even though C failed to load, he uses it anyway. The next piece of the bug is how Perl compiles subroutines and how it uses strict. I'm sure you're familiar with how C blocks work. Perl runs them, in order, at compile time. As it turns out, C means the same thing to perl as C. So what perl really does when it is asked to require C is: BEGIN { require Moose; Moose->import } BEGIN { use strict; *foo = sub { warn "IT WORKS" } } BEGIN { use strict; *get_id = sub { $self->id } } ... We die on the last line because strict kills us. Even though other stuff is in the module, it never has the chance to run, because the bad C definition unwinds the stack before we are even done processing C, much less processing the at-runtime stuff. The interesting part is that perl doesn't "roll back" the partially-loaded module state; everything that worked, works. You can call C just fine. The correct sub is in the symbol table. This is why you normally kill the program after a bad module load -- you are now in a somewhat random inconsistent state. And that's why I wasted an hour debugging my application. Someone forgot a simple C. I couldn't see the error with my program, and some of the stuff perl managed to compile worked just fine. (So why didn't C and C work? Simple; all the Moose sugar runs at runtime; after the all of the C blocks. C is just a regular old function call.) What you should take away from this is that you shouldn't roll your own module loading function. Just call C and everyone will be a lot happier. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkjd8tIACgkQ2rw+dVvzZm3XjQCeMcZRe6ji6ojRRh2VG1i94OcM c14AoKfqQcga4272vhliv7i7+36Q5AXg =DkSU -----END PGP SIGNATURE-----