In looking at exporter modules, I've also done a bit of thinking about private functions. A comment from DAGOLDEN on my function exporter post prompted me to try and enumerate the different ways you can do private functions in Perl 5.
Let's say you have a simple module which provides function foobar()
.
But Windows needs a different approach,
so you decide to have a private function for the windows
implementation,
which is called by the public function on Windows ($^O eq 'MSWin32'
).
There are other, possibly better, ways to approach this problem,
but let's not worry about that here.
The simplest approach is to just not make the private function available for import:
package Foobar;
use parent 'Exporter';
our @EXPORT_OK = qw/ foobar /;
sub foobar {
return windows_foobar(@_) if $^O eq 'MSWin32';
...
}
sub windows_foobar {
...
}
Presumably the documentation for the module would only talk about foobar()
,
and users of the module would only learn about
the existence of windows_foobar()
if they looked at the source.
There are at least two disadvantages with this approach. If you've been paging through the code and come across this function, there's nothing letting you know that it's a private function, unless the author added a comment.
And then when a potential user realises it's not exported, they can still use the function:
use Foobar ();
Foobar::windows_foobar(...);
And if they really want to import it into their namespace, they could write something like:
BEGIN {
use Foobar ();
no strict 'refs';
*windows_foobar = *{'Foobar::windows_foobar'}{CODE};
}
windows_foobar();
This is the same as the previous example, but the private function is defined:
sub _windows_foobar {
...
}
This is the convention that private functions have their names prefixed with an underscore. This addresses one of the disadvantages of the first solution presented, but isn't really much better. That said, much of Perl and the toolchain works on conventions, so you see this used a lot.
You can create an anonymous function which is only referenced via a lexical variable. This means it can't be accessed outside your module:
my $windows_foobar = sub {
...
};
sub foobar {
return &$windows_foobar(@_) if $^O eq 'MSWin32';
...
}
Unlike the previous two approaches, it really is private, and this works from Perl 5.6 onwards. But you have to at least declare the lexicals before the functions that use them, and the syntax is a bit clumsy: it doesn't feel like you're just writing private subs, it feels a bit grubby.
If you really don't like the calling syntax, and you're going to call a private function a number of times within your function, you could use a local typeglob alias:
sub foobar {
local *windows_foobar = $windows_foobar;
return windows_foobar(@_) if $^O eq 'MSWin32';
...
}
This is apparently slightly quickly than calling via the reference, but the downside of this is for that many readers you've just moved the WTF? to a different part of the code. So much so that it might make sense to put a comment by it.
Perl 5.18.0 introduced an experimental implementation of
lexical subroutines.
A private sub is prefixed with my
or state
. If you use state
,
you need to either use 5.010
(or later), or use feature 'state'
:
package Foobar;
use 5.010;
use parent 'Exporter';
our @EXPORT_OK = qw/ foobar /;
no warnings 'experimental::lexical_subs';
use feature 'lexical_subs';
state sub windows_foobar { ... }
sub foobar { ... }
This really is a private function: the user can't call it directly using the techniques shown in the first two sections.
There are still some disadvantages though. Firstly, it's an experimental feature, which requires Perl 5.18.0 or later, and the two lines of boilerplate. Also, private functions have to be declared before they're used. So either you have to put all your private functions ahead of the public ones, which feels the wrong way round, or you have to predeclare them as private:
state sub windows_foobar;
sub foobar {
...
}
sub windows_foobar {
}
Read the Perl 5.18.0 delta to learn more about lexical subs.
I think someone seeing state sub subname
might be unsure what that meant.
They might have a better idea on seeing my sub subname
, but I'd still
prefer to see private sub subname
.
Sub::Private gives you a :Private
attribute, which you use to
mark a sub as private:
use Sub::Private;
sub windows_foobar :Private {
}
With this you can put your private subs at the end of your module, and it works in Perl 5.8.9, and possibly earlier (I haven't checked). As with lexical subs, this declares a truly private sub.
This is the closest to what I'd like to write, but it introduces a number of dependencies: Sub::Private in turn uses Attribute::Handlers, namespace::clean, B::Hooks::EndOfScope and Sub::Identify.
namespace::clean lets you control whether your package symbols will appear in the symbol table or not. This is the module that provides the key underlying functionality for Sub::Private, described above.
package Foobar;
use parent 'Exporter';
our @EXPORT_OK = qw/ foobar /;
sub foobar { ... }
no namespace::clean; # everything above here stays in symbol table
sub windows_foobar { ... }
use namespace::clean; # from here up to previous call is 'cleaned'
This doesn't suffer from the ordering problem, and introduces fewer dependencies, but is less reader-friendly.
I like the solution offered by Sub::Private, but most of the time I wouldn't want to make the trade-off of private functions against the additional dependencies. Do you really want to pay that price to ensure that someone can't call your private functions? If you've made clear that certain functions are private, then caveat importer.
When the language offers non-experimental private functions I'll start using them, but not in CPAN releases for a good while. In the meantime, I'll use leading underscores, and if I really feel the need, lexical sub refs.
comments powered by Disqus