Keep it Clean
The Big Mess
Perl object orientation is a nice¹ simple toolkit. You just put some subroutines into a package and they become methods:
The problem is, we're adding subroutines to packages all the time that probably shouldn't be methods:
...and then much later...
See, the problem is that when you import subroutines into your class, they become methods. The ones above might not seem too bad, but quite often these non-method imports can lead to confusion:
...but later, someone misremembers the name of
comes_before and writes this:
This does something bizarre, because
before ends up referring to Moose's
before, used to apply method modifiers to a method.
To avoid these kinds of mistakes, one policy – and one to which I often adhere – is to not import anything. So, don't write this:
This makes it very clear where the routines live, but it's also a real drag to type, especially if you're going to use
trunc over and over. Another solution is to use Sub::Exporter or, if the module you're importing from uses Exporter, to use Sub::Import. Then you could say something like this:
With this formula,
$obj->_trunc will still work as a method call, but at least you can blame the programmer who tried calling a private method on your object... but remember that it might be you.
Rather than just never import, though, you can take an alternate route: you can delete the imported subroutines from the symbol table after you've bound to them. In other words:
If you do this, then later,
$obj->trunc will fail, finding no method. The call to
real_method still works, though, because the symbol
trunc (at line 12) is bound to the installed routine at compile time, but the
delete (at line 5) is not executed until run time. This is a big drag, though, because you have to account for all the stuff you imported and delete it all again.
That's why we have namespace::clean. We could replace the delete in the code above with
use namespace::clean. It would make a note that once the compile phase was over, all the subroutines defined before the
use would get deleted. That means that if you put all your subroutine-importing
use statements above namespace::clean, they'd all be purged and wouldn't be callable as methods.
Unfortunately, you might be importing some stuff that should be a method. Going back to Moose, for example, you don't want the
meta method that you get from
use Moose to get cleaned up. It would break... well, pretty much everything. Further, if you're going to have to purge more than one hunk of imports, you have to start doing some annoying accounting and use of
no namespace::clean. See the namespace::clean synopsis for a short example.
So, the next upgrade from namespace::clean is namespace::autoclean. Once you use it anywhere in your program, it will wait around until the compile phase is over, and will then look at all the subroutines in the package. It decides² which ones are methods and which ones are not, and then purges all the non-methods. This makes it much better at just doing the right thing without making you think about it. It won't purge
meta, it won't get confused because you accidentally move one of your subroutine-importing
use statements below it. The down side is that because it uses Class::MOP, it's got a decent-sized memory and compile-speed overhead. I'm almost always using Moose, though, so the cost is tiny compared to the sanity it provides.
Finally, I should note that sometimes you do want to import something so that it can be called as a method. Maybe you're using a library that exports subroutines that expect to be methods – Data::Section, for example. In these cases, if you're using namespace::autoclean, you'll need to make sure the subroutines get installed properly so that they won't be purged. Sub::Exporter::ForMethods exists for just this reason:
Well, not everyone agrees it's nice.
It uses the logic in Class::MOP::Mixin::HasMethods's
_code_is_mine, if you want to start seeing how it works – but that's a private method and might move around or change.