On p5p, David Golden suggested adding use feature 'method';
as a simple extension to Perl 5 which adds a method
keyword to declare a method, then shift off the invocant and make it available within the body of the method.
I wrote a patch to enable method
in feature.pm. The patch looks longer than it is. The important user-side example is in the simple test suite I added:
package SomeClass;
use strict;
use warnings;
use feature 'method';
sub new {
my ($class, $value) = @_;
bless \$value, $class;
}
method get_value {
return $$self;
}
method dup_value
{
return $$self
. reverse $$self;
}
method set_value :lvalue
{
$$self;
}
method ctor
{
my $value = shift;
bless \$value, $self;
}
package main;
my $sc = SomeClass->new( 'instance variable' );
can_ok($sc, 'get_value');
can_ok('SomeClass', 'set_value');
is($sc->get_value, 'instance variable', 'simple method should work');
is($sc->dup_value, 'instance variableelbairav ecnatsni',
'method using additional $self should work');
$sc->set_value = 'foo';
is($sc->get_value, 'foo', ':lvalue attribute should work');
$sc = SomeClass->ctor( 'method constructor' );
is($sc->get_value, 'method constructor',
'method should work for constructor too');
done_testing();
As you can see, with use feature 'method';
in scope, the method
keyword is a synonym for sub
which implicitly adds a single line at the start of the body of the method: my $self = shift;
.
Making this work meant adding method
to the tokenizer as a keyword recognized only in the scope with use feature 'method';
enabled. That explains the patches to lib/feature.pm, keywords.h, perl_keyword.pl, and regen/keywords.pl, as well as the
the second half of the changes to toke.c.
The first half of the changes to toke.c are a copy and paste and
specialization of the special handling of the sub
keyword. I had
to do this for two reasons. First, the sub
handling always
returns a token representing SUB
to the parser, and I needed to
return something special. I chose the token METH
instead.
Second, it makes little sense to support prototypes on methods, as Perl 5
cannot resolve prototypes across method dispatch. I removed the portion of the
sub
tokenizing related to that. (This batch of code isn't an
example of screechingly obviousness; it could become cleaner as I see more
unnecessary code to remove.)
With all of that in place, the tokenizer provides a stream of tokens to the acutal parser in perly.y. The patch adds an alternation to one production and two supplementary productions.
The alternation to statement processing starts when the parser encounters
the METH
token. A method starts with meth
, runs the
special startsubparse
production to set up the new subroutine
scope, has a name, has an optional attribute list, and then has a method body.
The latter is a new production.
A method body has curly braces delimiting it. It then runs a special new
production called addimplicitshift
(did I mention the phrase
"screechingly obvious"?), runs an existing production called
remember
to start a new lexical scope, then gathers a list of
statements, and finally encounters the ending curly brace.
The addimplicitshift
production is the most interesting part of this whole process. It uses the core lex_stuff_pvs()
function to add a single line of Perl 5 code: my $self = shift;
. Perl will parse that line as if it had always been the first line of the body of the method, even though the parser added it itself.
This means that if you wrote:
use feature 'method';
method some_method
{
my $self = shift;
...
}
... you'll get the warning you expect about a redeclaration of a lexical, with the correct the line number.
Does this work well with CPAN modules which declare their own
method
keywords? It's not incompatible with them, in the
sense that the only code in the world which gets any use out of use
feature 'method';
right now is the test in my patch. Yet something like
MooseX::Declare
may (I haven't tested it) be incompatible if and only if you use
MooseX::Declare
and use feature 'method'
in the same
scope.
A larger question is whether this is a valuable feature. Certainly it offers two modest advantages, by marking the intent of the developer to distinguish between a method and a function and by removing a line of boilerplate from every method. (Given that most of my methods are between three and ten lines long, saving double-digit percentages of their length is a tremendous savings of code.)
Will this feature get in Perl 5.14? I don't know. I have my doubts—the implementation could be cleaner, the ultimate syntax could change to use something other than my $self = shift;
, and there's undoubtedly a long line of people who believe that the only way to prototype a proposed change to the Perl 5 core is to write a CPAN module (at which point a long line of people is happy to say "Why would you want that in core? There's already a CPAN module for it!").
Regardless of all of that, I can think of a fair few advantages to doing something like this in the core. I'll explain those tomorrow.
I think the worst offender in "weird syntax", "new comers unfriendly" and "boilerplate" is rather the new constructor.
To which I would reply "you mean like the 7 modules on CPAN that already implement some super/subset o this behavior?" ... in fact I think I just did make that reply.
Hi chromatic
Ok, so you want to hide the line:
my $self = shift;
but it's going to confuse beginners. This makes me very sad.
The trouble is that it creates a type of action-at-a-distance, hence violating The Principle of Least Surprise, which in turn worsens Perl's reputation for being obscurantistic.
Cheers
Ron
Hi chromatic
After submitting, I get a confirmation page, which is good. But I can't see a logout button. Is this my problem (if, so, apologies), or a Chrome display problem, or the designer's?
Cheers
Ron
Ok, since we'll have "method", how about "class" also? (Note that Ruby has "class" but no "method").
I think you should call it `meth` not `method` it's much shorter, I don't want to type method ... :P
Ignore Caleb, everyone knows he just wants to make us all meth addicts. =P
Just a minor nit -- it certainly won't make it in for 5.14 as the "contentious code change" freeze point has past. But I hope the discussion will continue to see if it could be a part of 5.16 in 2012.
@stevenharyanto, what do you think "class" should do that "package" does not? Also, note that in 5.14, you can write this:
Anything inside the block is in the "Foo" namespace.
What do you think about second or after argument processing like MX::Declare?
is more pretty.
Forgive me if I'm missing something, I'm quite tired at the moment so my brain may not be working at full capacity. But is there any reason the new core keyword couldn't be implemented in a way so that extension could, for example, set $^H{OVERRIDE_KEYWORD}{method} = __PACKAGE__ (or something along those lines) to tell perl to deactivate parsing of the method keyword? This way I could do the following:
This would also make it possible to have a default method keyword and only use extended (and maybe more costly) syntax in a smaller scope.
Having recently adopted Method::Signatures::Simple, I am now completely addicted to the tedium-reducing method keyword and would be happy to see anything to increase its adoption in the Perl world, including this patch.
I posted a patch once to add a class keyword. I have no intention of resubmitting it.
I like that, but first things first. Getting a new keyword (even one enabled by feature) is difficult enough without doing additional work.
no feature 'method'; should suffice.
@dagolden: I think chromatic explains it pretty nicely (although the case is on "method"): http://www.modernperlbooks.com/mt/2011/01/what-else-a-method-keyword-could-do.html
TL;DR an explicit "class" keyword allows us to express the intention of creating a class instead of just a package, and other things can follow from that.
Instead of using the my $self = shift; as the method boilerplate, wouldn't $_ be less surprising? Since $_ is the default variable in most places, why not using it in method too?
I know that $_ won't have lexical scope as $self would if your patch was used, but this doesn't seem to be a problem in other uses of $_ and one can just add a my $self = $_; boilerplate on the first line of the method.
Just wondering whether i missed something...
That's an interesting idea. I'll think more on it.
My initial impression is mild dislike, though there is some prior art in Perl 6's implicit shortcut for attribute access (and the semi-magical method binding in languages such as Java).
The best argument for $self is the prior art in almost all Perl 5 OO code I've ever read.
Alex,
Would this invalidate the use of $_ within the method or at least make it *very* confusing? Think of the use of $_ in grep or map.
C.