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

package Bar; use Moose; sub bar { print "bar\n"; } package Foo; use Moose; sub foo { return [(Bar->new)x3]; # what magic do i need to apply here? } package main; Foo->new->foo->bar; #should print "bar\nbar\nbar\n" instead of failing + with "Can't call method "bar" on unblessed reference"
In words: I want ARRAY to know that when I do $array->foo, where $array is an array ref and foo is an arbitrary method, it should magically do map { $_->foo } @$array


holli

You can lead your users to water, but alas, you cannot drown them.

Replies are listed 'Best First'.
Re: how to create a "delegating" array?
by Corion (Patriarch) on Nov 29, 2009 at 08:40 UTC

    I guess you'll need to return another object (untested):

    package MapDelay; sub new { my ($class,@self) = @_; bless \@self, $class; }; sub AUTOLOAD { (my $m = $AUTOLOAD) =~ s/.*:://; my $self = shift; map { $_->$m(@_) } @$self }; package Foo; sub foo { return MapDelay->new( Bar->new, Bar->new, Bar->new ); };

    Also, Bar->new x3 stores three copies of the same object. I'm not sure if you wanted that.

      No, I wanted independent instances. Your code got me in the right direction. It was a little buggy, though =). Moosified version:
      package Bar; use Moose; has x => ( is => 'rw', isa => 'Any' ); sub bar { print "bar", shift->x, "\n"; } package MapDelay; use Moose; use vars '$AUTOLOAD'; has elements => ( is => 'rw', isa => 'ArrayRef' ); around BUILDARGS => sub { my ($orig,$class) = @_; $class->$orig(elements => [splice @_,2]); }; sub AUTOLOAD { (my $m = $AUTOLOAD) =~ s/.*:://; map { $_->$m(@_) } @{shift->elements} }; package Foo; use Moose; sub foo { return MapDelay->new( Bar->new(x=>1), Bar->new(x=>2), Bar->new(x=> +3) ); }; package main; Foo->new->foo->bar; #prints bar1\nbar2\nbar3\n


      holli

      You can lead your users to water, but alas, you cannot drown them.
        Bar->new(x=>1), Bar->new(x=>2), Bar->new(x=> +3
        this can be good place to use map:  map Bar->new (x => $_), 1 .. 3
Re: how to create a "delegating" array?
by spx2 (Deacon) on Nov 29, 2009 at 16:30 UTC
    hi holli,

    with minimal changes(blue parts) to your initial code

    package Bar; use Data::Dumper; use Moose; sub bar { my $arg = shift;
    if(ref($arg) eq 'ARRAY') { return map { $_->bar; } @{ $arg }; }
    print "bar\n"; } package Foo; use Moose; sub foo { return [(Bar->new)x3]; # what magic do i need to apply here? } package main;
    use autobox ARRAY => 'Bar';
    Foo->new->foo->bar; #no more "Can't call method "bar" on unblessed reference" #because now ->bar is a native method of ARRAYREFs. #actually all the methods of Bar are native methods on ARRAYREFs now.
      This requires the "consuming" code to know about the internals of 'Bar' (by using autobox). That is clumsy and not what I want.


      holli

      You can lead your users to water, but alas, you cannot drown them.
        you can write code completely independent of 'Bar' if you use monkeypatching combined with autobox, is that what you mean ?