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

Update: tlm slaps forehead. Of course! Thanks ikegami and friedo!

Update 2: Fixed error pointed out by japhy. Thanks.


(NB: The background for this question is this recent snippet by merlyn.)

Is there any fundamental difference between

{ no strict 'refs'; @methods = grep defined &{ "$pkg\::$_" }, keys %{ "$pkg\::" }; }
and
{ no strict 'refs'; @methods = grep $pkg->can( $_ ), keys %{ $pkg . '::' }; }
? The second form looks to me a bit easier to understand at first glance, but I'm not 100% certain that it is equivalent to the first one.

the lowliest monk

Replies are listed 'Best First'.
Re: defined &{ $pkg . '::foo' } vs. $pkg->can( 'foo' )
by friedo (Prior) on Jul 14, 2005 at 20:36 UTC
    Yes, the first tells you if a certain sub is defined in a certain package. The second tells if you a class can call a particular method. The main difference is that the second one knows about inherritence.
Re: defined &{ $pkg . '::foo' } vs. $pkg->can( 'foo' )
by ikegami (Patriarch) on Jul 14, 2005 at 20:35 UTC

    can could potentionally match subs in parent classes, such as test in the following example:

    package pkgA; sub test {} package pkgB; our @ISA = qw( pkgA ); our @test; { no strict 'refs'; @methods = grep $pkg->can( $_ ), keys %{ $pkg . '::' }; }
Re: defined &{ $pkg . '::foo' } vs. $pkg->can( 'foo' )
by dragonchild (Archbishop) on Jul 14, 2005 at 20:50 UTC
    There's an additional issue - use of can() depends on a given method which can be overridden to do whatever the package author feels like. Now, this can be useful in the case of AUTOLOAD. However, you may not want AUTOLOADed methods to be brought over in that snippet (nor can some of them be brought over).

    The defined way that merlyn did is the safest for general use (I would think), but there are specialized uses that may prefer can().


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: defined &{ $pkg . '::foo' } vs. $pkg->can( 'foo' )
by tlm (Prior) on Jul 14, 2005 at 22:27 UTC

    Thanks for all the replies. The problem that served as context for the question is to devise an automatic way to define delegated methods.

    A typical "delegated method" looks something like:

    sub frobozz { my $self = shift; return $self->{ _delegate }->frobozz( @_ ); }
    ...where $self->{ _delegate } holds a reference to some other object. The first object basically passes the buck to the delegate.

    My basic idea was to run through all the "public methods" that the delegate can perform (by plucking them out of its and its ancestors' symbol tables) and define a delegated method (like the one illustrated above) for each of those methods among these that the containing class cannot perform.

    The fly in the ointment, of course, is that fishing functions out of symbol tables, in general, cannot distinguish between public methods (which are part of the delegate's API) and private methods or functions that have been imported by the delegate class, and neither of which belong to its API.

    The only thing I can think of is that, if all the ancestor classes are ones that I control, then I can define those classes so that their private methods are easy to distinguish (e.g. with leading underscores) and so that they don't import anything. They either use fully qualified function names, or do something like:

    BEGIN { *_croak = \&Carp::croak; }
    I.e. import a function "by hand" but rename it in such a way that it can be easily filtered out.

    (Or I can go nuts and assign all these non-API methods to lexical scalars.)

    And yes, the whole scheme described above would miss any AUTOLOADed or runtime-defined methods of the delegate object. Again, I don't know a way around this, other than avoiding using such methods in those classes I control.

    I suspect that, as is my habit, I am complexifying things... As always, your comments are most welcome.

    the lowliest monk

      OK, here I'm replying to my reply to a post of mine. Pretty solipsistic... Never mind me.

      One could argue that automating the generation of delegated methods should give off some pungent code-smell... Why? Because the only rationale for it is for the delegating class to be able to "track" the delegate class's API automatically, but if that's the case, maybe the delegating class should be inheriting instead. In other words, one could argue that the portion of a class A's API that is delegated to another class B should be decided by the designer of class A, irrespective of the changes in class B's API. (Of course, class B's API could shrink, which could in turn break some delegated methods, but IMO it is far more likely that, if it changes at all, the API of class B will grow, not shrink.) Therefore, instead of having class A automatically figure out which methods to delegate to the delegate class, these methods should be hard-coded in the definition of class A. This obviates the problem of distinguishing those functions in the delegate class's and its parents' symbol tables that belong to the delegate class's API from those that don't...

      the lowliest monk

Re: defined &{ $pkg . '::foo' } vs. $pkg->can( 'foo' )
by japhy (Canon) on Jul 14, 2005 at 21:07 UTC
    The first one does not mesh with your node's title. It says "of the symbols in $pkg, which are defined as functions in this current package?". If you write it as you did in your node title, grep defined ${$pkg . "::$_"}, keys ..., then it would be saying "of the symbols in $pkg, which are defined as functions in $pkg?".

    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
Re: defined &{ $pkg . '::foo' } vs. $pkg->can( 'foo' )
by jdporter (Paladin) on Jul 14, 2005 at 20:54 UTC
    O.k., you've gotten the answer to the underlying question, but you might still wonder what is the difference between those snippets of code, in terms of their practical effect. The first one answers the question, "Of the symbols defined in the given package, which ones are functions? (vs. scalars, arrays, etc.)" The second one answers the question, "Of the methods that the given class can handle, which ones are defined in the class, vs. in some ancestor class." It's worth noting that these tests can be fooled by AUTOLOAD and other aspects of the run-time dynamicity of perl.
      I have to pick a nit. The second one doesn't answer "of the methods that the given class can handle, which ones are defined in the given class?". The (psuedo)code for that would be @local = grep defined &{$class . "::$_"}, $obj->list_all_my_methods. The second one answers the question "of all the symbols in the given class, which ones are names of methods of the given class?".

      Case in point:

      package Parent; sub foo { 1 } package Child; @ISA = 'Parent'; sub new { bless {}, shift } $foo = 10; package main; my $pkg = "Child"; print join(", ", grep defined &{ $pkg . "::$_" }, keys %{ $pkg . '::' +}), "\n"; print join(", ", grep $pkg->can( $_ ), keys %{ $pkg . '::' }), "\n";
      This prints "new" on the first line, and "new, foo" on the second. I changed the defined() test a bit, because with $pkg in there, it's saying "of the symbols in $pkg, which are defined as functions in this current package I'm in?" rather than "... which are defined as functions in $pkg?".

      Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
      How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
      It's worth noting that these tests can be fooled by AUTOLOAD and other aspects of the run-time dynamicity of perl.

      The second one fails only if you write broken code.

Re: defined &{ $pkg . '::foo' } vs. $pkg->can( 'foo' )
by ysth (Canon) on Jul 15, 2005 at 12:31 UTC
    $ perl -w sub foo; sub AUTOLOAD { print "called $AUTOLOAD" } print "is foo defined? ",0+defined &foo, " does foo exist? ", 0+exists + &foo, "\n"; foo(); __END__ is foo defined? 0 does foo exist? 1 called main::foo
    You almost always want to use exists &subname rather than defined &subname; there's no advantage in excluding declared-but-not-defined subs.

      You almost always want to use exists &subname rather than defined &subname; there's no advantage in excluding declared-but-not-defined subs.

      Hmmm... I don't see how including declared-but-not-defined subs could be a good idea either for the particular application I had in mind or for the one I cited.

      the lowliest monk

        I don't see why. Pre-declaring subs is the responsible way to use AUTOLOAD. Why would you want to disallow it in your delegatees? Anyway, this qualifies as a difference between ->can and defined&.