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

First, thank you guys very much for answering questions. It's very appreciated.

I've been playing around with MooseX and I'm confused as to a problem with the following code (perl 5.12.2 on a linux box, btw):

use strict; use warnings; use MooseX::Declare; class Foo { use MooseX::Types::Moose qw(Num); method print_number (Num $num) { print "Yep. ($num) is a number.\n\n"; } method regex_number_then_print (Num $num) { $num =~ /(\d*\.\d+)/; print " ** I found ($1) during regex.\n"; $self->print_number($1); } method regex_number_then_print_with_quotes (Num $num) { $num =~ /(\d*\.\d+)/; print " ** I found ($1) during regex.\n"; $self->print_number("$1"); } method regex_number_then_print_with_intermediate_var (Num $num) { $num =~ /(\d*\.\d+)/; print " ** I found ($1) during regex.\n"; my $tmp = $1; $self->print_number($tmp); } } package main; my $obj = Foo->new; print "###### Directly printing number #####\n"; $obj->print_number('1.7'); print "###### Directly printing quoted number #####\n"; $obj->regex_number_then_print_with_quotes('1.7'); print "###### Directly printing number after intermediate variable lau +ndering #####\n"; $obj->regex_number_then_print_with_intermediate_var('1.7'); print "###### Directly printing number from capture variable \$1 ##### +\n"; $obj->regex_number_then_print('1.7');
My output is this:
plxc16479> tmp12.pl ###### Directly printing number ##### Yep. (1.7) is a number. ###### Directly printing quoted number ##### ** I found (1.7) during regex. Yep. (1.7) is a number. ###### Directly printing number after intermediate variable laundering + ##### ** I found (1.7) during regex. Yep. (1.7) is a number. ###### Directly printing number from capture variable $1 ##### ** I found (1.7) during regex. Validation failed for 'Tuple[Tuple[Object,Num],Dict[]]' with value [ [ + Foo=HASH(0x63d490), "coerce" ], { } ], Internal Validation Error is +: [+] Validation failed for 'Tuple[Object,Num]' with value [ Foo{ }, " +coerce" ] [+] Validation failed for 'Num' with value coerce at /nfs/pdx/disks/ +nehalem.pde.077/perl/lib64/site_perl/MooseX/Method/Signatures/Meta/Me +thod.pm line 435 MooseX::Method::Signatures::Meta::Method::validate('MooseX::Me +thod::Signatures::Meta::Method=HASH(0x1cbefd0)', 'ARRAY(0x1cbf330)') +called at /nfs/pdx/disks/nehalem.pde.077/perl/lib64/site_perl/MooseX/ +Method/Signatures/Meta/Method.pm line 151 Foo::print_number('Foo=HASH(0x63d490)', 1.7) called at tmp12.p +l line 15 Foo::regex_number_then_print('Foo=HASH(0x63d490)', 1.7) called + at tmp12.pl line 41

It seems to indicate the call is being made with the correct values, judging from this line:

Foo::print_number('Foo=HASH(0x63d490)', 1.7) called at tmp12.p

Why the error when printing directly from the $1 capture variable? Why does it work when the capture variable is quoted or intermediated? Am I doing something wrong?

Replies are listed 'Best First'.
Re: a MooseX Q that's driving me up the wall :)
by ikegami (Patriarch) on Dec 03, 2010 at 16:43 UTC
    sub f { 'xxx' =~ /(.*)/; print("$_[0]\n"); } 'aaa' =~ /(.*)/; f($1); # xxx 'aaa' =~ /(.*)/; f("$1"); # aaa

    Perl passes arguments by reference, not by value. You are passing a global variable, and the global variable gets changed the by the called function before the argument is used. Passing a copy of the global avoids the problem.

      That makes sense. And quoting the global like "$1" would result in a literal string being passed, taking the global out of the result. Thanks!
        A string literal is a piece of code, so it's not a string literal being passed. The string literal, however, does create a new string. That's what's being passed.
      Huh, I thought that MooseX::Declare just copied to the method's args. But it aliases them instead, like they're aliased in @_? That's pretty cool.
Re: a MooseX Q that's driving me up the wall :)
by Anonymous Monk on Dec 03, 2010 at 03:22 UTC
    I don't recommend using the number globals in the first place. When I replace $1 with $foo in the code, and then add my ($foo) = in front of each occurance of $num =~, the problem goes away.

    It's almost as if MooseX::Declare is doing something magical with $1, and it's somehow aliased until that point, which is why it apparently shows the right value in the call stack until it's replaced by the string "coerce".

      I find it somewhat scary that MooseX seems to be messing with some pretty standard Perl magic. I don't think I've ever seen odd nonstandard behavior from regex captures like that before.

      I did notice that the value suddenly shifted to the 'coerce' string. In my more complicated code this is pulled from, I actually saw 'is_subtype_of'. So there's definitely something strange going on in the background. I'm hoping a guru can shed some light on it. I don't like wierd voodo stuff going on that I can't figure out.

      When you say that you don't recommend the numbered capture globals, do you mean in general or specifically when dealing with MooseX? Do you always use your own variables for regex captures? Why? Not being critical, I just like learning and I'm still firmly an early intermediate Perl programmer (by my own guesstimate).

        When you say that you don't recommend the numbered capture globals, do you mean in general or specifically when dealing with MooseX? Do you always use your own variables for regex captures? Why? Not being critical, I just like learning and I'm still firmly an early intermediate Perl programmer (by my own guesstimate).
        In general. I always store the return value of the matched regex (usually in lexical variables), which eliminates a lot of confusion: you know in what scope the variable storing the captured value is, unlike with the globals, and you can do more regex matching without worrying about overwriting previous values. Also, it's simply unnecessary to do
        $string =~ /$pattern/; my $match_foo = $1; my $match_bar = $2;
        when you can do

        my ($match_foo, $match_bar) = $string =~ /$pattern/;

        The relevant programming patterns I use the most probably look something like like

        if (my ($foo, $bar) = $input =~ /$pattern/) {

        and

        my @matches = grep { condition } map { /(foo)(bar)/ } @data;