perl5.10, give back our $_

perl5.10 added given keyword. very nice.

However, given("foo") does my $_ = "foo"(lexical $_) implicitly. This means it does not work code using local $_ in given block. like this:

use 5.010;

sub foo(&$) {
    my ($code, $value) = @_;
    local $_ = $value;
    $code->();
}

foo { print $_ } "foo"; # => "foo"

given ('xxx') {
    foo { print $_ } "foo"; # => "xxx"
}

this occurs even List::Util.

use 5.010;
use List::Util qw/first/;

given ('xxx') {
    my $test = first { $_ eq 'xxx' } qw/ bar baz /;
    # $test => bar
}

or Try::Tiny (POD described this point.)

use Try::Tiny;

given ('xxx') {

    try {
        die "error";
    } catch {
        warn $_; # => "xxx"
    }

}

I think we can avoid unnecessary problems by given() make local $_ instead of lexical $_.

What do you think?

of course, It may be there is the purpose of lexical scope that I have not understand.(I dont read all p5p logs.)

and, We can use global $_ this way even inside lexical $_ scope.

given ('xxx') {
    foo { our $_; print $_ } "foo"; # => "foo"
}

or

given ('xxx') {
    foo { print $::_ } "foo"; # => "foo"
}

3 Comments

Ugh. This might be a case of “we didn’t think about that so it seemed like a good idea”, but I’m not sure. The thing is that given copies the value into $_, unlike foreach which aliases $_ to the value.

I suggest you file a bug against Perl, and let p5p hash it out.

In the meantime, note that you can just as well use for('xxx') anywhere you would write given('xxx'), and the code will in practice work identically. (The difference is what happens if the code inside the block modifies $_, and whether global references to $_ work. And that some people find your code weird when you use foreach on single values.)

Not sure if the whole 'lexical $_' thing was thought out completely beyond 'seems like a good idea', including the part about 'our $_' restoring global $_ (so if you want to localise the value passed to 'given' so it can be used in the dynamic, not lexical scope of the given block, you need to do something unpleasant like 'local our $_ = $_' or 'local $::_' as 'local $_' throws 'Can't localize lexical variable $_')

given's semantics can be useful, but sometimes aren't. Mixing two very different scoping systems can be confusing. Fortunately, you can use for instead of given if you want the traditional dynamic scope.

Leave a comment

About Naoki Tomita

user-pic A perl programmer who learn ninjutsu at the weekend.