Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

'around' method modifier does not seem to work

5 views
Skip to first unread message

Marcos Barbeitos

unread,
Aug 6, 2015, 7:15:02 PM8/6/15
to mo...@perl.org
Howdy,

I looked up the behavior of the modifier 'around' in <http://search.cpan.org/~ether/Moose-2.1600/lib/Moose/Manual/MethodModifiers.pod#Around_modifiers>, and the code snipet is:

  around 'size' => sub {
      my $orig = shift;
      my $self = shift;

      return $self->$orig()
          unless @_;

      my $size = shift;
      $size = $size / 2
          if $self->likes_small_things();

      return $self->$orig($size);
  };

In my code, I have:

has 'sequence' =>
(
    is => 'rw'
  , isa => 'Str'
  , predicate => 'has_sequence'
);

around 'sequence' => sub
{
    my $orig = shift;
    my $self = shift;
    my $sequence = uc shift;

    # Do lots of things with $sequence and then

    return $self->$orig( $sequence );
}

But the attribute is not set.

I've tried lots of variations of the last line:

$self->$orig( $sequence );
return $orig->( $self, $sequence );
$orig->( $self, $sequence );    
return $sequence;

With no success, as expected. However, if I do:

around 'sequence' => sub
{
    my $orig = shift;
    my $self = shift;

    return $self->$orig( @_ );
}

The attribute is set and life goes on. Of course, that does not work for me because I need to do a bunch of things to the argument passed to this method.

Any ideas about the reasons for the (apparent?) discrepancy in behavior?

Best wishes and thanks in advance.

--
Marcos S. Barbeitos

Departamento de Zoologia - Sala 360
Setor de Ciências Biológicas
Universidade Federal do Paraná
Caixa Postal 19020
Curitiba, PR 81531-990
Brazil

Phone: (55 41) 3361-1634

Ben Tilly

unread,
Aug 6, 2015, 10:15:02 PM8/6/15
to Marcos Barbeitos, mo...@perl.org
Stupid guess.

You set the attribute successfully then unset it when you fetch it because you dropped the test for @_ that tells you not to set it then.

Chris Prather

unread,
Aug 7, 2015, 12:45:04 PM8/7/15
to Marcos Barbeitos, mo...@perl.org
So you'll need to provide a reduced example that demonstrates the behavior your showing. When I tried to reproduce (with the script below) the attribute was being set just fine.

#!/usr/bin/env perl
use 5.12.1;
use warnings;

{

    package AroundTest;
    use Moose;

    has foo => ( is => 'rw' );
    around foo => sub {
        my ( $next, $self, $seq ) = @_;
        $seq = uc($seq);
        $self->$next($seq);
    };
}

my $at = AroundTest->new();
$at->foo('bar');
say $at->dump;

Ben Tilly

unread,
Aug 7, 2015, 2:45:02 PM8/7/15
to Chris Prather, Marcos Barbeitos, Moose
Change your example to say $at->foo instead of $at->dump to see the bug.

Add a check in the around to only try to set foo if @_ actually had 3
elements. Then the bug should go away.

I suggest that the documentation add a comment like this:

# Return the existing value if we are not trying to change it.
return $self->$orig()
unless @_;

That might keep someone else from making the same mistake.

Chris Prather

unread,
Aug 7, 2015, 5:15:01 PM8/7/15
to Ben Tilly, Moose, Marcos Barbeitos
If you check the part he quoted in the original email, that is in there. Obviously three of us missed it so maybe it could be highlighted better.

-Chris

Kent Fredric

unread,
Aug 7, 2015, 10:45:01 PM8/7/15
to Chris Prather, Ben Tilly, Moose, Marcos Barbeitos
On 8 August 2015 at 09:09, Chris Prather <peri...@prather.org> wrote:
> If you check the part he quoted in the original email, that is in there.
> Obviously three of us missed it so maybe it could be highlighted better.


I think there's confusion stemming from there being *two* methods
being wrapped, one has the guard, and the other has its internals
elided, and we assume there is a guard.

Method one is called "size":

around 'size' => sub {
my $orig = shift;
my $self = shift;

return $self->$orig() ##### Guard
unless @_;

my $size = shift;
$size = $size / 2
if $self->likes_small_things();

return $self->$orig($size);
};


Method two is called "sequence":

around 'sequence' => sub
{
my $orig = shift;
my $self = shift;
my $sequence = uc shift;

# Do lots of things with $sequence and then
#### NO CLEAR GUARD

return $self->$orig( $sequence );
}



And as he describes, the issue he experiences is predominantly with
the second of these, which makes sense, given there is no code that
*we* can see with the guard mechanism.



--
Kent

KENTNL - https://metacpan.org/author/KENTNL

Marcos Barbeitos

unread,
Aug 8, 2015, 4:30:02 PM8/8/15
to Kent Fredric, Chris Prather, Ben Tilly, Moose
Howdy, everybody, thank you so very much for your help.

Ben was indeed correct; if I do:

around 'sequence' => sub
{
    my $orig = shift;
    my $self = shift;

    if (  @_ == 1 )
    {
my $sequence = _get_parsed_sequence( shift );
        return $self->$orig( $sequence );
    }
    else
    {
        return $self->{sequence};     
    }
};

I get it to work.  Not a very elegant solution, for I must access the attribute directly instead of using an accessor. 

One issue still remains though, if I pass the argument to the constructor:

my $collection = DCSE::Collection->new
(
   id => 'test'
 , sequence => $sequence
 , helix => $dcse->helix
);

I believe that 'around' is not called, for I get the unparsed argument when I fetch the attribute. 

I will look into 'writers' and 'readers', maybe they will work better in this case?

Cheers!

Buddy Burden

unread,
Aug 8, 2015, 7:45:02 PM8/8/15
to Marcos Barbeitos, Kent Fredric, Chris Prather, Ben Tilly, Moose
Marcos,

> around 'sequence' => sub
> {
> my $orig = shift;
> my $self = shift;
>
> if ( @_ == 1 )
> {
> my $sequence = _get_parsed_sequence( shift );
> return $self->$orig( $sequence );
> }
> else
> {
> return $self->{sequence};
> }
> };
>
> I get it to work. Not a very elegant solution, for I must access the
> attribute directly instead of using an accessor.

Well, you have the accessor right there, if you'd like to use it: it's
called `$orig`:

return @_ ? $self->$orig( _get_parsed_sequence(shift) ) : $self->$orig;

> One issue still remains though, if I pass the argument to the constructor:
>
> my $collection = DCSE::Collection->new
> (
> id => 'test'
> , sequence => $sequence
> , helix => $dcse->helix
> );
>
> I believe that 'around' is not called, for I get the unparsed argument
> when I fetch the attribute.

One of the few useful things I learned from C++ that's applicable to
Moose: initialization != assignment. In C++ you'd be stuck with
duplicating some code, but in Moose ...

> I will look into 'writers' and 'readers', maybe they will work better in
> this case?

Look into "triggers." ;->


-- Buddy

John Macdonald

unread,
Aug 9, 2015, 5:15:02 PM8/9/15
to Chris Prather, Marcos Barbeitos, mo...@perl.org
As Ben suggested, setting the attribute is working fine; but any read of the attribute sets it to undef.  I added two lines to the end of Chris' test:

...
say $at->dump;
say $at->foo;
say $at->dump;

Now running it gives:

perl test.pl
$VAR1 = bless( {
                 'foo' => 'BAR'
               }, 'AroundTest' );

Use of uninitialized value $seq in uc at test.pl line 13.

$VAR1 = bless( {
                 'foo' => ''
               }, 'AroundTest' );

Write-only attributes are generally not very useful.  :-)

John Macdonald
Software Engineer

Ontario Institute for Cancer Research
MaRS Centre

661 University Avenue

Suite 510
Toronto, Ontario

Canada M5G 0A3

Tel:

Email: John.Ma...@oicr.on.ca

Toll-free: 1-866-678-6427
Twitter: @OICR_news


www.oicr.on.ca

This message and any attachments may contain confidential and/or privileged information for the sole use of the intended recipient. Any review or distribution by anyone other than the person for whom it was originally intended is strictly prohibited. If you have received this message in error, please contact the sender and delete all copies. Opinions, conclusions or other information contained in this message may not be that of the organization.


From: Chris Prather [peri...@prather.org]
Sent: August 7, 2015 12:27 PM
To: Marcos Barbeitos
Cc: mo...@perl.org
Subject: Re: 'around' method modifier does not seem to work

Karen Etheridge

unread,
Aug 9, 2015, 5:15:02 PM8/9/15
to Marcos Barbeitos, mo...@perl.org
On Thu, Aug 6, 2015 at 4:04 PM, Marcos Barbeitos <msbar...@gmail.com> wrote:
>     around 'sequence' => sub
>     {
>         my $orig = shift;
>         my $self = shift;
>         my $sequence = uc shift;
>
>         # Do lots of things with $sequence and then
>
>         return $self->$orig( $sequence );
>     }
>
>     With no success, as expected. However, if I do:
>
>     around 'sequence' => sub
>     {
>         my $orig = shift;
>         my $self = shift;
>
>         return $self->$orig( @_ );
>     }
>
>     The attribute is set and life goes on. Of course, that does not work for me because I need to do a bunch of things to the argument passed to this method.

The only difference I see between these two subs is the argument passed to $orig.
So, have you tried printing it to check that it is what you expect it to be?

Karen Etheridge

unread,
Aug 9, 2015, 5:15:03 PM8/9/15
to Marcos Barbeitos, Kent Fredric, Chris Prather, Ben Tilly, Moose
> On Sat, Aug 8, 2015 at 1:18 PM, Marcos Barbeitos <msbar...@gmail.com> wrote:

>     I get it to work.  Not a very elegant solution, for I must access the attribute directly instead of using an accessor.

No, instead of
>             return $self->{sequence};     
you should do
              return $self->$orig();
to use the proper reader mechanism.


>     One issue still remains though, if I pass the argument to the constructor:
>     my $collection = DCSE::Collection->new
>     (
>        id => 'test'
>      , sequence => $sequence
>      , helix => $dcse->helix
>     );
>
>     I believe that 'around' is not called, for I get the unparsed argument when I fetch the attribute.

This should be fixed when you fix the code path for the reader as above.

Marcos Barbeitos

unread,
Aug 10, 2015, 3:15:01 PM8/10/15
to Karen Etheridge, Kent Fredric, Chris Prather, Ben Tilly, Moose
Hi Karen and everybody else,

Thanks for the tip, it works as expected, except when I pass the attribute to the constructor.  In that case, I get the original sequence, and not its parsed version.

I added a print statement to the 'around' modifier so that I could keep track of how many times 'around' was called.

I noticed that the modifier was called when you set the attribute and everytime you fetch it, but not when it is passed to the constructor. As Buddy noticed, maybe it really shouldn't be.

No biggie, though, I found out that I may actually have to re-structure the code and I'll probably have no need for the modifier after I do.

Again, many thanks to all!

Kent Fredric

unread,
Aug 10, 2015, 10:30:02 PM8/10/15
to Marcos Barbeitos, Karen Etheridge, Chris Prather, Ben Tilly, Moose
On 11 August 2015 at 07:02, Marcos Barbeitos <msbar...@gmail.com> wrote:
> Thanks for the tip, it works as expected, except when I pass the attribute
> to the constructor. In that case, I get the original sequence, and not its
> parsed version.
>
> I added a print statement to the 'around' modifier so that I could keep
> track of how many times 'around' was called.
>
> I noticed that the modifier was called when you set the attribute and
> everytime you fetch it, but not when it is passed to the constructor. As
> Buddy noticed, maybe it really shouldn't be.
>
> No biggie, though, I found out that I may actually have to re-structure the
> code and I'll probably have no need for the modifier after I do.


That's because the constructor doesn't set the attributes by passing
them to $object->method($value), but instead does some magic in
$object->meta-> ...

What it seems like you really want is some sort of Type Coercion, so
that when data of a given type is passed to the constructor, the type
coercion unpacks it into a form suitable.
0 new messages