Subroutine Signatures - my Plan (v.1)

There has been quite a lot of discussion on p5p about subroutine signatures, so I figured I'd lay out my current vision here with as much details as I can. As more of this gets hashed out on p5p, I expect I'll add notes at the bottom (can I do that?) pointing to a blog entry for Subroutine Signatures - my Plan (v.2), etc.

All of this is what I'm working on, but I don't have a commit bit, so it's not going anywhere without getting thoroughly vetted and blessed first.

I'm posting my code at http://github.com/PeterMartini/perl, in the peter/signatures branch (meant to be kept in tandem with doy/subroutine-signatures).

GOAL:

Part 1:

In the scope of use feature "signatures" (or whatever)

sub foo($bar,$baz) {}
equivalent to sub foo { my ($bar, $baz) = @_; }
sub foo($bar,@baz) {}
equivalent to sub foo { my ($bar, @baz) = @_;}
sub foo($bar,%baz) {}
equivalent to sub foo { my ($bar, %baz) = @_;}

Part 2:

Add a *new* way to access this information. This does not replace prototype, and is not meant to. I'd vote for 'signature', either as a keyword or in some namespace (Scalar::Util?)

I haven't thought this one through yet, but the big concern will be making sure it doesn't block future growth.

Part 3:

For backwards compatability, add a proto attribute to allow for the old behavior.

sub foo($bar,$baz) : proto($$) {}
equivalent to sub foo($$) { my ($bar,$baz) = @_;}

I don't plan to protect this by a feature.

I don't care either way whether:

sub foo($$);
sub foo($$) : proto($$){}

is legal or not, but will allow it as long as all of the prototypes match.

Note that:

sub foo($$);
sub foo($bar,$baz) : proto($$) {}

MUST have a proto attribute, or it will die due to the prototype mismatch (prototype would be none)

Part 4:

In the check phase of compilation, implicitly convert:

sub foo { my ($bar, $baz) = @_;}

to (internally) sub foo($bar,$baz){}

If and only if the assignment is the first statement of the sub, the named variables are the first entries in the PAD (for technical reasons) and @_ is not modified.

SELECTED POINTS OF CONTENTION:

1. Q: What is the value, if its just saving keystrokes?
Possible A: It's wanted often enough that several CPAN variants exist
Possible A: It *may* improve performance
Possible A: It adds another method of introspection, allowing the code to formally declare its own parameters.

2. Q: Will this prevent custom CPAN modules from implementing their own syntax?
My A: No. The signature will be controlled by a feature, which means if a 3rd party module is used, it can simple shut off the built in signature.

3. Q: Should a named parameter list *be* the prototype (accessible through prototype(\&sub), though not necessarily affecting parsing)?
My A: For backwards compatibility reasons, no. The current prototype is a simple string; I think it may make much more sense to expose the signature information as a string an array of hashes (depending on wantarray / GIMME), to allow room for growth.

4. Q: Do we really want to create lexical variables without an explicit ‘my’?
My A: I originally allowed for an explicit my, but there seemed to be no other reasonable options so I'd dropped it as redundant.

5. Q: What about @_?
My A: At this stage, I have no intention of touching it. It will continue to be available for read-write access, and it is necessary if a sub wants to check the count of arguments it was passed (sub foo($bar) would have no way of seeing the second, third, etc argument)

I'm sure I missed plenty of questions and answers - I'll add them either here or in future posts as I spot them.

14 Comments

I don't see the point of this (other than to look like other languages). If we need subroutine signatures, why not make it an attribute :signature(%whatever, ...) in Attribute::SubSignatures or whatever and have that package autoloaded on feature=signature.

I think this is a great idea. This reminds me a lot of the argument list that JavaScript uses. Please keep up the work; between your hard effort and p5p's feedback something great will come out of it.

The one point I'd like to add about @_ is that if we were to choose to not populate it, that could possibly speed up function calls by a significant amount. I am thinking it could get to ten of percent of a function call.

TOTALLY unfair mock-up: A normal function call:

time perl -e 'sub bar {my ($a, $b, $c) = @_;} for (1..1000000) {bar(qw(1 2 3))}' real 0m0.461s

And a function call bypassing @_ for arguments (this also saves pushing onto the stack, which we wouldn't normally):

time perl -e '@a = qw(1 2 3); sub foo {my ($a, $b, $c) = @a;}for (1..1000000) {foo()}' real 0m0.404s

So yeah, it's not a clear case, but this saves over 10% of the OVERALL run time, so not setting up @_ ought to give us a very measurable uplift. Cachegrind/callgrind seem to agree, too. It's one of the most significant opportunities for optimization in the past years and I'm thinking it's too good to turn down.

I did something similar in my Sub::Starter module; only I called them usage statements. Sub::Starter is a filter that converts usage statements into sub skeletons. It is easier to write a usage statement than a consistent sub & its documentation.

See https://www.metacpan.org/module/Sub::Starter

Please note that a usage statement is not valid Perl.

Awesome, this feature has been mentioned as a TODO item in the Perl documentation since the 90s. Really hope this time it will get past the naysayers, keep up the great work!

I think native subroutine signatures are a great idea, but I'm concerned about the effect of replacing the prototypes with a different feature, even if it's controlled by a lexical pragma, That means the thing after your sub name has a different meaning from one place in your code to another, which would make Perl's already obfuscated argument handling even more opaque, especially to new users.

I would suggest introducing a new keyword rather than overloading existing syntax. For example, Method::Signatures introduces a new func keyword for non-method subs.

Personally I don't see why we are reinventing the wheel here. Method::Signatures and MooseX::Method::Signatures have been working at this for a while now. They have nearly identical semantics and those few differences can be settled. Why not just add those semantics (including keywords, as friedo says) to the core under the feature pragma. Why have CPAN if not to work out these issues first, then we COREize those that work best, giving the wide availability and increased speed (seeing as it works at parser level, not as a module). Just my $0.02.

What about references? Could I do?

sub foo(\%hash1, \%hash2, \@arr) {}

Will it automatically turn calls like:

foo(%hash1, %hash2, @arr); into:

foo(\%hash1, \%hash2, \@arr); ???

Or will they just collapse into one list like perl does now?

Perl has tons of "action at a distance," so some more may be ok. Or perhaps not, because smartmatching has been determined to be "bad," so that will probably change in future versions.

I second Joel Berger's vote for reusing Method::Signatures & MooseX::Method::Signatures. I have not used either, but they're pretty awesome. Why not just translate them from Devel::Declare and perl into the craziness of perl's C implementation?

We would also get a method keyword too!!! That could perhaps also create $self for us! No my $self = shift; crap anymore. You could also combine this with the work that's going into the perl5-mop, which will probably involve implementing a class keyword too!

Also, Method::Signatures supports type constraints such as Int or Str just like Moose does. If perl5-mop supports such constrains, then perhaps you could add that as well.

I agree with whoever mentioned on p5p (In the p5p weekly summaries as I don't follow it myself.) that they don't want code that simply replaces:

sub foo {
my ($arg1, $arg2) = @_;
}

with:

sub foo($arg1, $arg2) { }

That's almost completely useless, and simply reduces typing without actually adding any beneficial features.

Thank you for working on this. The CPAN options are very good, but this really seems like it should be a core feature.

What I would really like to see is support for named parameters as is implemented in MooseX::Method::Signatures. That really promotes clarity in the method/sub call.

Seems like a lot of the comments here are missing the fact that this is a simple first step that can be expanded later on. But things like method keywords, type constraints, etc need other features in Perl core to really work (like a MOP or actual types, etc).

I like this idea because it starts simple but opens the gates for future improvements and optimizations without precluding them. And if we get this into core those other project won't have to reinvent the wheel to get signatures

You are doing language design, this is not something that can start simple and then grow. It just doesn't work that way.

You have to plan the the new feature on the whole, consider all the things you want to support on the long run, and then, define an implementation plan that is where you can really start simple and then improve on it. Otherwise you will soon get caught on the backward compatibility trap.

My impression is that this feature is being driven by implementation, you just saw a low hanging fruit and went for it.

This feature is driven by “this is the syntax every signature implementation on CPAN agrees on, and which every proposal on p5p (going back years and years) agrees on, so let’s leave out all of the features that nobody can agree on so we can finally have the features that nobody disagrees about, instead of never getting them because of all of the extra features people also want but can never agree on”.

Hence to say “consider all the things you want to support on the long run” is to say “please waste a bunch of time so you can then fail like everyone else before you”, or, alternatively, “I would like to never have signatures in core Perl 5, ever”.

You are doing language design, this is not something that can start simple and then grow. It just doesn't work that way.

Funny. This syntax is implemented by every signature module on CPAN. And then every signature module on CPAN adds different bits to the syntax on top of that (with lots of overlap). So the design of this feature can obviously only be grown in practice – not in theory. It just doesn’t work that way. In theory.

So the logic is: If someone disagrees with adding new syntax for signatures we are left in the stone age with this ridiculous oversimplifying proposal? Design by fear and ignorance, not even by committee?

Signatures are the best chance to optimize perl function and method calls, since functions without sigs block all optimizations possibilities. There is really no chance to look into each function for argument assignments. Ditto for return types.

Please talk at least to the perl6 people about the possibilities we have with proper sigs, if you do not understand me.

We have been better in 2001.

Is staying in the mesozoic era with nothing at all better than an insufficiently ambitious proposal? We could have had this much syntax 4 or 5 years ago if it wasn’t for the facts that a) people would never let a proposal stick to the basics (either the proposers wanted more, or other people wanted it to do more) and b) people could never agree on anything beyond the parts that this proposal specifies. Instead we continue to suffer my ($foo, $bar, $baz) = @_ for the foreseeable future. Is that really what we want, collectively?

Leave a comment

About Peter Martini

user-pic I blog about Perl.