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

Edit: Please see below for the solution.

O monks,

My program has a loop in which it instantiates a Moo object and composes in one or another role based on some condition.

Debugging shows that each time through the loop a new object is instantiated, and the correct role is composed in, but when I call a method on the object, the first object's method gets the call! Even though it is a new instance of the object and the old one has gone out of scope since we are in the next iteration of the loop.

It's acting as if the method is compiled into memory. I feel like this is so weird it *must* be something simple I'm overlooking, but I can't for the life of me see what. Any help to understand would be greatly appreciated.

Here's code that demonstrates the issue: MyClass.pm :

package MyClass; use Moo; sub BUILD { my ( $self, $args ) = @_; my $module = $args->{'number'} % 2 ? 'Odd' : 'Even'; warn "$args->{'number'} $self loading $module"; eval "with '$module'; 1;" or die $@; } 1;
Odd.pm :
package Odd; use Moo::Role; warn "Loaded ", __PACKAGE__; sub bar { my ( $self, $foo ) = @_; warn "$foo $self I am Odd"; } 1;
Even.pm :
package Even; use Moo::Role; warn "Loaded ", __PACKAGE__; sub bar { my ( $self, $foo ) = @_; warn "$foo $self I am Even"; } 1;
test.pl :
#!usr/bin/perl use strict; use warnings; use MyClass; foreach my $foo ( 1 .. 2 ) { print "\n"; my $o = MyClass->new({ number => $foo }); warn "$foo $o->bar"; $o->bar( $foo ); print "\n"; } __END__
Output, NOT as expected. Note the object memory address is different second time through, and the correct module is loaded, but the sub that gets called is from the other package!
$ perl test.pl 1 MyClass=HASH(0x118df00) loading Odd at MyClass.pm line 8. Loaded Odd at Odd.pm line 4. 1 MyClass=HASH(0x118df00)->bar at test.pl line 13. 1 MyClass=HASH(0x118df00) I am Odd at Odd.pm line 7. 2 MyClass=HASH(0x11b68e8) loading Even at MyClass.pm line 8. Loaded Even at Even.pm line 4. 2 MyClass=HASH(0x11b68e8)->bar at test.pl line 13. 2 MyClass=HASH(0x11b68e8) I am Odd at Odd.pm line 7.

Thank you!

Note: this post was edited, including title, to remove extraneous references to Parallel::ForkManager which I thought was the issue. Thanks to Athanasius for pointing out that that was unrelated. )

EDIT: thanks to a push from Athanasius and a giant assist from mst on IRC, I figured out the correct way to do what I want, which is to use Role::Tiny->create_class_with_roles to compose the Role into an anonymous class before calling new().

So now MyClass.pm looks like:

package MyClass; use Moo; 1;
and the caller looks like:
#!usr/bin/perl use strict; use warnings; use MyClass; use Role::Tiny(); foreach my $foo ( 1 .. 2 ) { my $module = $foo % 2 ? 'Odd' : 'Even'; my $class = Role::Tiny->create_class_with_roles( 'MyClass', $modul +e ); my $o = $class->new(); $o->bar( $foo ); } __END__
Output:
$ perl test.pl 1 MyClass__WITH__Odd=HASH(0x23f5f80) I am Odd at Odd.pm line 8. 2 MyClass__WITH__Even=HASH(0x2442db0) I am Even at Even.pm line 7.


The way forward always starts with a minimal test.
  • Comment on SOLVED [ create_class_with_roles() ]: Moo[se] namespace weirdness with conditionally composed-in Roles
  • Select or Download Code

Replies are listed 'Best First'.
Re: Namespace weirdness with Parallel::ForkManager using 0 children
by Athanasius (Archbishop) on Mar 21, 2016 at 06:58 UTC

    Hello 1nickt,

    This behaviour does not appear to be due to Parallel::ForkManager. I can reproduce it with just the following test script:

    #! perl use strict; use warnings; use MyClass; for my $foo (1 .. 2) { my $o = MyClass->new({ number => $foo }); $o->bar($foo); }

    Output:

    16:56 >perl 1576a_SoPW.pl 6052 1 MyClass=HASH(0x4b4ca8) loading Odd at MyClass.pm line 8. 6052 Loaded Odd at Odd.pm line 4. 6052 1 MyClass=HASH(0x4b4ca8) I am Odd at Odd.pm line 7. 6052 2 MyClass=HASH(0x2274048) loading Even at MyClass.pm line 8. 6052 Loaded Even at Even.pm line 4. 6052 2 MyClass=HASH(0x2274048) I am Odd at Odd.pm line 7. 16:56 >

    Changing from Moo to Moose makes no difference to the result.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thanks Athanasius, I should have thought of testing that, was so sure it was P::M-related. I'm going to edit the post and the title to try to draw in some Moo experts. Thanks again.

      The way forward always starts with a minimal test.

        Adding the following line at the end of sub BUILD in “MyClass.pm” seems to fix the problem:

        Role::Tiny->apply_roles_to_object($self, $module);

        So my guess is: roles are, by default, class-based, rather than object-based, so that once a role has been added to a class, the definition of that class doesn’t change. But with multiple threads, the problem doesn’t arise as the class definition is re-created for each new thread.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: Moo[se] namespace weirdness with conditionally composed-in Roles (two roles with the same name, with alias)
by Anonymous Monk on Mar 21, 2016 at 07:47 UTC

      Thank you for your reply. That's not what I am attempting though: I want to load only one Role, based on a condition.


      The way forward always starts with a minimal test.
      Naturally adding  with 'Odd', 'Even'; yields  Due to a method name conflict between roles 'Even and Odd', the method 'bar' must be implemented by 'MyClass' :)

        Right. I'm not trying to load more than one Role with a method of the same name, though. I want to load one or the other and have the parent execute the loaded module's methods when called. Looks like I can get what I want with apply_roles_to_object() so long as I am willing to accept the Roles overwriting the parent's methods in case of conflict (which is the opposite of a Role as far as I know, but I can live with it).

        Thanks for your reply.


        The way forward always starts with a minimal test.