A Method::Signatures Retrospective

As some of you may know, I worked on a partial rewrite of Method::Signatures last year, mainly to add Moose types to the sigs, but also to do some tweaks here and there, and to use it as a base for Method::Signatures::Modifiers (included with MS), which can replace MooseX::Method::Signatures inside MooseX::Declare.  This latter reason was the primary goal for me, and I’ve gleefully been using MS, and MSM, in practically all the code I’ve written since.  I’ve also ported over a rather large codebase (although admittedly not much of it was using MXD).  We’re almost at the eleven month mark since our first release of the new MS, so I thought it might be interesting to check in with how things are doing.

First of all, we haven’t seen too many bugs.  A couple of obscure things here and there, but nothing earth-shattering.  So that’s positive.  I noticed a fairly early adoption by Joel Berger, and he seems to be using it in many of his code snippets that he posts.  So there’s a plus as well.  And I just received a feature request from the Damian, who said in his opening remarks, “I adore Method::Signatures and am now using it in nearly all my own development...and in absolutely all my teaching.” So that’s pretty nice.

If you have found MS and/or MSM to be helpful in your development, I encourage you to leave a short note in the comments, and I’d also like to ask your opinion on a small design issue.  This has been bugging me for a few months now, and I brought it up with Damian, and then schwern opened a GitHub issue for it.  Well, technically he reopened an issue that I had opened way back when I was first digging into MS.  Back then, I didn’t really have an opinion on it.  But now I do.

Here it is.  Let’s start with some basic stuff.  This:

method foo ()
{
}
means that you’re declaring a method which allows no parameters.  If you did try to pass it a parameter, you’d get an error like so:
In call to Test::foo(), was given too many arguments, it expects 0 at ./test.pl line 16.
So far so good.  Now, this:
method foo (@_)
{
}
is the MS way of saying, “I don’t actually care what my parameters are.” It’s the no-signature signature, so to speak, and the only real advantage it has over just falling back to sub is that it still shifts off $self for you.  (And also it maintains some consistency, in that it might look weird if every method is a method except for that one, which is a sub.  But that’s just sugar.) You can call that with whatever parameters you like; MS doesn’t bother to check them.

With me so far?  Good.  Now here’s the sticky part.

What does this one mean?

method foo
{
}
Well, as far as MS is concerned, it means the same as method foo (): that is, it expects no arguments, and blows up if you try to pass any.  But that’s not consistent with other systems that have signatures.  For instance, in Perl 6, no signature just means ... well, no signature.  That’s also true of MooseX::Method::Signatures, which means that this creates yet another difference between MXMS and MSM, which is always slightly irksome.  That is, P6 and MXMS treat method foo as equivalent to method foo (@_), except that they don’t actually recognize that particular syntax.  P6 will (I believe) just shift off $self for you, and MXMS won’t even do that (that is, it sets $self, but it doesn’t shift it off, so @_ will still contain $self).


This seemingly trivial change can trip you up in surprising places.  For instance, this (which you may recognize from Moose::Manual::Construction):

around BUILDARGS
{
    if ( @_ == 1 && !ref $_[0] )
    {
        return $self->$orig( ssn => $_[0] );
    }
    else
    {
        return $self->$orig(@_);
    }
}
won’t do what you expect with MSM, because the extra arguments to BUILDARGS results in the “too many arguments” error.  Likewise, this, which is pretty much straight from the MooseX::App::Cmd POD:
method execute
{
    my $result = $self->blortex ? blortex() : blort();
     
    recheck($result) if $self->recheck;
          
    print $result;
}
will blow up, because the App::Cmd underneath is passing parameters to execute which you don’t actually need.  At least in the BUILDARGS example, you were actually referencing @_; in this one, adding a signature of (@_) doesn’t seem to make much sense, but it’s what you need to do to make it work with MS or MSM.


So why wouldn’t we change it?  Well, one thing to consider is backwards compatibility.  We’ve had it working this way for the nearly-a-year that I’ve been involved, and I’m pretty sure it was working that way before I got here as well.  So changing it might surprise some people that have gotten used to it working the way it does.  Now, one good thing about it is that it’s not going to add a fatal error into your existing code.  Rather, it would be removing a fatal error ... and, since it was, in fact, a fatal error, you probably already removed it, so most likely this change would impact any old code at all.  Still, there could be something, somewhere, that perhaps does a method call in an eval block, then checks $@ and expects to find the “too many arguments” error ... okay, it’s a long shot, but, as Judy Tenuta used to say: it could happen!

So, if you have an opinion on whether we should make this change or not, we’d love to hear it.  Comment below, or join the discussion on GitHub.  And thanks for using Method::Signatures!

2 Comments

Buddy - are you be able to comment on the plans for Windows support for Method::Signatures? The dependency on Devel::BeginLift is currently a problem on Win32. I note there is an issue to fix this: https://github.com/schwern/method-signatures/issues/39

It would be great to be able to use this in Windows.

Leave a comment

About Buddy Burden

user-pic 14 years in California, 25 years in Perl, 34 years in computers, 55 years in bare feet.