Avoid accidently creating methods from module exports

Perl’s object system is fuzzy. Methods are really just subroutines and classes are just packages, which means that any subroutine in a package is also a method in that class. Your class might have subroutines that you’ve never even noticed, so you end up with methods that you didn’t want in your interface.

There are several reasons to not allow methods you don’t specifically want to create:

  • If you don’t want the method, you shouldn’t have it. That’s the obvious one.
  • If you don’t have the method in your API, your subclasses shouldn’t have it either.
  • It’s organizationally messy, even if you never look under the hood.
  • To some people, it’s morally suspicious if not a sin against Programming.

.

To begin, create a subroutine that shows you all of the subroutines defined in a package:

use 5.010;

my @subs = get_defined_subs( 'main' );
say join "\n", @subs;

sub get_defined_subs
	{
	my( $package ) = @_;

	my @subs;
	no strict 'refs';
	foreach my $name ( keys %{"${package}::"} ) # symbol table magic
		{
		next unless defined &{"${package}::$name"};
		push @subs, $name;
		}
		
	@subs
	}

Don’t worry about the symbol table magic there: that’s not the point of this Item. Try the subroutine yourself. With just that script, you see only the show_defined_subs since that’s the only subroutine you’ve defined:

show_defined_subs

Now create a test class. Call it Class::Dirty (since you are going to make a mess in it), and put that in Class/Dirty.pm in the same directory as your program so you find it in the current directory. To start, simply add a couple of methods to the class:

package Class::Dirty;

sub new  { ... }
sub init { ... }

1;

Note that that literal code actually compiles under Perl 5.12, since that ... is the “yadda yadda” operator that stands in for a real statement during compile time but dies at run time. That version isn’t quite out as this makes it to the website, but it’s Real Soon Now.

Now use show_defined_subs with Class::Dirty:

use Class::Dirty;

my @subs = show_defined_subs( 'Class::Dirty' );

say join "\n\t", "Defined subs:", @subs;

sub get_defined_subs
	{
	my( $package ) = @_;

	my @subs;
	no strict 'refs';
	foreach my $name ( keys %{"${package}::"} )
		{
		next unless defined &{"${package}::$name"};
		push @subs, $name;
		}
		
	@subs
	}

As you might expect, you now see two subroutines names in the output. Those are the methods from Class::Dirty:

Defined subs:
	new
	init

Since subroutines are also methods, instead of just checking that a subroutine is defined, check that the class responds to that method by checking can. It’s a one line change:

use Class::Dirty;

my @subs = get_defined_subs( 'Class::Dirty' );

say join "\n\t", "Available methods:", @subs;

sub get_defined_subs
	{
	my( $package ) = @_;

	no strict 'refs';
	foreach my $name ( keys %{"${package}::"} )
		{
		next unless eval { $package->can( $name ) }; # <---
		push @subs, $name;
		}
		
	@subs;
	}

You get the mostly same output because nothing has changed in Class::Dirty. All of the subroutines it defines are also methods, so everything works out:

Available methods:
	new
	init

Now it's time to make a mess. Add the Fcntl to your module and use the seek set of constants (or anything else that exports stuff):

use 5.010;

package Class::Dirty;

use Fcntl qw(:seek);

sub new  { ... }
sub init { ... }

Running the script again, you see that you have several new methods:

Defined subs:
        SEEK_SET
        new
        SEEK_END
        SEEK_CUR
        init

That's no good. Since Perl doesn't distinguish between subroutines and methods, any subroutine, even the imported one, are methods. You probably don't want that.

You could import nothing by using an empty import list and use the full path specification to get to anything you want to use but that's not very pretty:

use Fcntl ();

...;

seek( $fh, 5, Fcntl::SEEK_CUR );

There's a better way to handle this though. The namespace::clean pragma can help. When you invoke it with use, it remembers the names for the previously defined subroutines and schedules the unbinding of those names at the end of the scope (and remember what defines your scope). This module is a bit odd because it works with things that have already happened instead of things that are going to happen. You use it when you want to unbind the subroutine names that you have already defined. In the Class::Dirty case, you'd load it right after you load modules that exported names. Change your package name to Class::Clean since it's no longer dirty:

package Class::Clean;

use Fcntl qw(:seek);

use namespace::clean;

sub new  { ... }
sub init 
	{
	...;
	seek( $fh, 5, SEEK_SET ); # SEEK_SET still there
	}

The actual subroutines (the code, not the names) are still there and the code in the scope can still use these subroutines by calling them as subroutines, but they aren't available later as method calls because their names have disappeared and Perl won't be able to resolve the names to code at runtime.

How does that work? First, remember that subroutine names and the code that goes with them aren't the same thing. Represent the subroutines foo and bar in PeGS. The pointy boxes show the name bound to the boxes that have the actual code. The bar subroutines calls the code that has the name foo:

two-subs

If you unbind the name from the subroutine after you compile, perl doesn't care because it already knows how to get to the code:

two-subs-one-name

The use namespace::clean bit doesn't really do anything other than track the list of defined subroutines. The module doesn't actually remove them until the end of the compile cycle for its scope. That way, you can use the subroutine names in the code and perl can use the names to find to the subroutine definitions. Once bound, however, perl doesn't need the names any more because it already knows where the code is. When perl gets to the end of the scope your during the compile phase, use namespace::clean unbinds that list of names from the subroutine definitions.

It's easy to see this scoped behavior in action. Start without any blocks, and you can see that SEEK_SET is still around:

package Clean;
use strict;
use warnings;

use Fcntl qw(:seek);

use namespace::clean;

print "Seek set: ", SEEK_SET, "\n"; # SEEK_SET still there

1;
# namespace::clean cleans up now, at end of file scope

Now use namespace::clean in a naked block and the SEEK_SET disappears at the end of that block:

package Class::Clean;
use strict;
use warnings;

{
use Fcntl qw(:seek);

use namespace::clean;
} # namespace::clean cleans up now, at end of block scope

print "Seek set: ", SEEK_SET, "\n"; # Compilation error!

1;

This doesn't even compile because SEEK_SET is now a bareword (where before Exporter took care of all of that).

Now you want to define your methods, but you don't want those to disappear. After you've defined them when namespace::clean does its work. to save them, invoke no namespace::clean. It figures out what you've defined since the last use namespace::clean and ingores those names so it won't unbind them later. Remember, namespace::clean looks backward. After the no, you can define more subroutines. If you invoke use namespace::clean again, it remembers the names of those new subroutines for unbinding at the end of scope:

package Class::Clean;
use strict;
use warnings;

# all of your imports go here
use Fcntl qw(:seek);

use namespace::clean; # unbind any names defined so far
# namespace::clean list starts anew

sub new  { 1 };
sub init { 1 };
sub foo  { 
	_just_a_sub_not_a_method(); # called as sub, not method
	printf "SEEK_SET is %d\n", SEEK_SET;
	};

no namespace::clean; # don't unbind the latest list of names
# namespace::clean list empty

# all of your private subs (not private methods) go here
sub _just_a_sub_not_a_method {
	# this is a normal sub
	print "I'm in _just_a_sub_not_a_method\n";
	}

use namespace::clean; # unbind the latest list of names

1;

Write a small program to test it:

use Class::Clean;

print "I'm in main\n";

Class::Clean->foo;

Class::Clean->_just_a_sub_not_a_method; # won't find this

That almost works. From foo you can call _just_a_sub_not_a_method because namespace::clean had not already removed it when perl compiled that part. At the end of the file, however, the name is gone, so when you call Class::Clean->_just_a_sub_not_a_method, perl can't resolve the method name.

I'm in main
I'm in _just_a_sub_not_a_method
SEEK_SET is 0
Can't locate object method "_just_a_sub_not_a_method" via package "Clean" at clean.pl line 7.

So there you have it. You don't have to let other modules create methods by importing into your namespace, and you can create private subroutines that only your scopes can access. You don't pass on a dirty API to any subclasses. Your Programming karma increases. Life is grand.