http://www.perlmonks.org?node_id=344294

tilly has asked for the wisdom of the Perl Monks concerning the following question:

If Foo inherits from Bar, and Bar defines an AUTOLOAD, then both undefined functions and methods in Foo can trigger Bar::AUTOLOAD. In some situations Bar::AUTOLOAD really wants to do different things on a function call than a method call. (Like issue a warning for a function, and try to handle methods.)

Which raises the issue of how you tell them apart.

Unfortunately caller doesn't do the trick. One approach that I thought of was Devel::Caller, but its test suite fails on my machine. (And it has documented serious limits.) There is a warning Use of inherited AUTOLOAD for non-method %s() is deprecated that is triggered exactly when I want to distinguish the cases. I don't know how to get at that, and it might be turned off. (Turning it on and trapping it could work, if I could think of a way to guarantee that behaviour.)

That leaves heuristics. If the first argument is blessed (testable with Scalar::Util) then it is a pretty safe bet that it is meant as a method call. Conversely if the first argument is the name of a package that exists, I'd guess that that is meant as a method call. What would people prefer to see assumed, though, if the string is one that plausibly could be a package (ie it matches a pattern like /^\w+(?:::\w+)*(?:::)?\z/). Should I assume that it is a method call on a package that doesn't yet exist, or assume that it is a function call? Or don't assume and make my error message ambiguous??

For the curious, this is for a proof of concept UNIVERSAL::AUTOLOAD_CAN, as described in Re: Re: Why breaking can() is acceptable.

  • Comment on Detecting function vs method in an AUTOLOAD?

Replies are listed 'Best First'.
•Re: Detecting function vs method in an AUTOLOAD?
by merlyn (Sage) on Apr 11, 2004 at 23:19 UTC
    I hope you never figure this out. There's a lot of code out there that presumes that nothing can detect the difference between method and subroutine calls. This is deliberate, and is thus a feature, not a bug.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      It is documented that AUTOLOAD got inherited for functions by accident, that feature is deprecated, and it will break at some point in the future.

      Why then should we actively discourage authors of AUTOLOADs who want to try to match future behaviour, now? Offering people no way to smooth the transition between how their code acts now and how it will act in some future release seems like a pretty serious omission to me.

      Furthermore it means that adding an AUTOLOAD can introduce many unintended effects that you have no way around. (Which is probably one reason why inherited AUTOLOADs for functions is now deprecated.)

      My attitude is this. A normal method has no way of knowing what package you tried to call it in. However an AUTOLOAD method has that information in $AUTOLOAD. Why? Because details of how you dispatched to AUTOLOAD can very reasonably affect what that AUTOLOAD should do. While I agree that a normal function or method should not guess, in an AUTOLOAD you may want that information for similar reasons.

      There's a lot of code out there that presumes that nothing can detect the difference between method and subroutine calls. This is deliberate, and is thus a feature, not a bug.
      Thus the resulting mess in CGI.pm for example, trying to distinguish between method and function, is by design? Jeezes.
      From the viewpoint of Perl 5, it's a feature, but from the viewpoint of Perl 6, it's a bug. Perl 6 will distinguish AUTOMETH from AUTOSUB. (Those are the declarative forms, which return references. Perl 6 will distinguish those from the defining forms, which are spelled AUTOMETHDEF and AUTOSUBDEF, and which may only be called on predeclared (or autodeclared) names to supply a missing definition.)
        So, there'll be ways to "fake" a method call using a subroutine call, like we have now? There'll have to be, or else you can't get Perl5 semantics inside Perl6 syntax, as promised.

        For example, this should still work, somehow:

        my $meth = $object->can("foo"); $meth->($object, $arg1, $arg2); # simulate $object->foo($arg1, $arg2);

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.