In Speeding
Up My Test Suite by 25%, I lamented that writing modern Perl code often
means using code which mangles the language at use
time. Consider
typical Moose code:
package MyApp::App::Role::HasMailer;
# ABSTRACT: role which provides other app roles with a mailer
use Modern::Perl;
use Moose::Role;
use MyApp::Mailer;
has 'mailer', is => 'ro', lazy_build => 1;
sub _build_mailer
{
my $self = shift;
my $config = do 'myapp_local.pl';
my $mail_client = $config->{'Model::UserMail'}{mail_client};
return MyApp::Mailer->new( $mail_client );
}
1;
... where functions such as has
and with
and
extends
look like declarations (such as my
and
sub
), but are actually code to run.
The difference is important. A declaration that's part of the language's grammar need only be parsed to have its effects take place, while a statement or expression needs to run to take place.
This is, of course, why variable declarations makes lexical scoping trivial
to understand for experienced Perl programmers, why binding closures to their
lexical environments is easy in the simple cases, and why binding closures to
their lexical environments when you use the STRING form of eval
is
so difficult.
As Moose hackers will tell you, Moose isn't a simple preprocessor you run over your code once to generate longer, uglier code without syntactic goodness. Instead, classes are built, not declared.
This is endemic to Moose, but it's not a characteristic specific to Modern Perl. It's inherent in Perl 5 itself, even the 1994 version. Perl 5 lets you run arbitrary code while parsing happens.
While you can gain tremendous flexibility with this approach (building up closures with partially applied arguments to export to caller namespaces), you can't serialize the resulting code as easily because you may have to run arbitrary code to restore your program in memory.
Put another way, Exporter is a library while it should be core language behavior.
In the simple case where a library wants to export a couple of symbols into
the caller's namespace, say min
and max
from List::Util, why is there
no simple syntax for marking those symbols as exportable in
List::Util
without having to run code (or worse yet, inherit
from) Exporter
?
If a declarative syntax existed—with language support or at least the broad consistency to allow for tool support—such that it were statically possible to determine the symbols exported from one package and imported into another package, precompilation would be easier. A module could have a static list of exports provided in a manifest of sorts. (A good optimizer could even decline to import unused code!)
I realize it isn't always possible to reduce all cases of exporting to a
list of static symbols (nor is it desirable to remove that power), but at least
80% of the code I write would benefit from this. We'd also be able to have
better tool support for the language; it would be easier to discover which
symbols come from where, and we might even close the "You can't always tell
which of two interpretations of a parse tree is valid in Perl 5 code thanks to
import
and BEGIN
" gap a little further.
(... but then someone will ask why strict
is a pragma and not
core language behavior, especially after looking at its implementation, and
then we'll all go to the paint store to pick out our favorite colors. See also "I didn't provide an example of this declarative syntax, so you can't argue about it in the comments.")
Larry punted on everything of this type by implementing "use" as instantly-executed but still vanilla Perl. I agree this was sweeping the complexity under someone else's rug. In fairness, when 5.000 was created, he had no way to know what would end up a good idea. I think we do now; it's a matter of tuition now, I think. Actually isn't there at least one exporter that uses attributes?
I've seen but not used Attribute::Exporter. I would have used it, but I needed to export partially applied closures in that project.
Thank goodness I appreciate irony.
Hi,Chromatic,seems the Declarative Exporting is amazing!Yes it is I want to ask:"why strict is a pragma and not core language behavior",I will see your refer post.