A better AUTOLOAD #73

Open
schwern opened this Issue Jan 25, 2010 · 4 comments

Comments

Projects
None yet
2 participants
Contributor

schwern commented Jan 25, 2010

The way one writes AUTOLOAD sucks. Particularly having to parse out $AUTOLOAD.

Implement method_does_not_exist() to replace AUTOLOAD(). It can be implemented by writing Object::AUTOLOAD() which calls method_does_not_exist(). The critical difference being that we want to provide the package and method name without having to parse it out. This should be stored in a message object. This can either be passed into method_does_not_exist() or supplied as a global. I'm leaning towards passed in as it avoids having to declare the magic global.

Here's an example of an automatic accessor.

sub method_does_not_exist {
    my $self = shift;
    my $msg  = shift;
    my $name = $msg->name;  # the method name, without the package qualifier

    if( @_ ) {        
        $self->{$name} = shift;
    }
    return $self->{$name};
}
Contributor

notbenh commented Mar 2, 2010

I guess I'm unclear on what builds $msg, would it be some underlying magic or is the assumption that its what was passed?

Idealy I think that the simplest thing would be do allow for something simple like :


use constant LOCALAUTOLOAD => [qw{plural_method_aliases some_other_fix}];

sub plural_method_aliases {
   my ($self,$missing_method) = @_;
   my $plural_aliases = {
      things => thing,
      ... 
   };
   if(defined $plural_aliases->{$missing_method}) ) {
      my $method = $plural_aliases->{$missing_method};
      return [$self->$method];
   } 
   ...
}

Contributor

schwern commented Mar 2, 2010

The thought about $msg was for it to be an object representing the method which the caller attempted to call. "message" being Smalltalk terminology, sorry. See issue 80 for information about the proposed Method object.

To your suggestion above, is it enough information to pass in just the method name and not the fully qualified method name? I'm not sure. The nice part about passing in a Method object is it can be string overloaded to return the method name. So the example reduces to:

sub method_does_not_exist {
    my $self = shift;
    my $method  = shift;

    if( @_ ) {
        $self->{$method} = shift;
    }
    return $self->{$method};
}

I don't think we need to multiple possible autoload methods with custom names. That really isn't solving a real problem with AUTOLOAD.

It means we'd have to rewrite the whole mechanism to do our own searching through a classes whole ISA tree calling eval { $class->LOCALAUTOLOAD } on each and seeing if they exist and if they do calling them in turn until one of them works. This will be slow. And how will we know if it works? Your code above suggests autoloaded methods would always have to munge their return value to be inside an array ref. This seems an unnecessary contrivance and wrecks havoc with subtle things like context. Instead of simply returning you have to manually check the context. It also means an autoload method cannot simply goto another method, adding overhead and leaving the autoload method on the call stack.

Contributor

notbenh commented Mar 2, 2010

I completely understand the desire to not reinvent some AUTOLOAD-like system but with the method_does_not_exits method you propose is this just a cheap way to build up accessors for class data.

Also I can see the confusion about my example, what I was proposing is that if I get the method name requested then I can just redirect that request as needed, though I'm wondering if the usecase you have in mind and the one that I'm thinking thru are just different?

Contributor

schwern commented Mar 2, 2010

Sorry, I guess the example was not clear. method_does_not_exist() IS the new AUTOLOAD(). The code I gave is an example of how a user might use it to write autoloaded accessors, it wasn't intended to show code that would be in perl5i. It is effectively AUTOLOAD with a better name and without the need to parse out the $AUTOLOAD global.

Here's an example of how you'd write your plural method aliasing.

sub method_does_not_exist {
    my $self   = shift;
    my $plural = shift;

    state %singulars = (
        things      => thing,
        geese       => goose,
    );

    if( my $singular = $singulars{$plural} ) {
        return $self->$singular(@_);
    }

    # A hypothetical meta method which returns the correct error
    # message for a method not being found.
    croak $self->mo->method_not_found_msg($plural);
}

You can also optimize it by building new static methods on the fly.
In this case a simple alias will do, made easier by perl5i's built in
aliasing.

    if( my $singular = $singulars{$plural} ) {
        my $code = $self->can($singular);
        $code->alias($plural);
        return $self->$code(@_);
    }

It really works just like AUTOLOAD, but more convenient.

Your example appears to be missing the bit that causes the redirect. How does it know to go to plural_method_aliases() or to some_other_fix()? Is this something you couldn't just do inside method_does_not_exist()? How often do you really want AUTOLOAD to do two radically different things?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment