develooper Front page | perl.perl5.porters | Postings from February 2013

Re: Salvaging lexical $_ from deprecation

Thread Previous | Thread Next
From:
Ruslan Zakirov
Date:
February 20, 2013 12:33
Subject:
Re: Salvaging lexical $_ from deprecation
Message ID:
CAMOxC8sJ0CnxpdFf6jE2C=Bv=q5YGazAYU=4CcEK=K-0X4tTYw@mail.gmail.com
On Wed, Feb 20, 2013 at 1:59 PM, Aristotle Pagaltzis <pagaltzis@gmx.de> wrote:
> * Rafael Garcia-Suarez <rgs@consttype.org> [2013-02-20 08:25]:
>> On 20 February 2013 07:51, Jesse Luehrs <doy@tozt.net> wrote:
>> [...]
>> > And this is exactly what breaks things. How is B any different from
>> > something like
>> >
>> >   my $_ = ...;
>> >   # ...
>> >   try {
>> >       require Foo;
>> >   }
>> >   catch {
>> >       die $_ unless /Can't locate .* in \@INC/;
>> >   }
>> >
>> > This will not work as expected, because the catch block will close
>> > over the lexical $_.
>>
>> As I mentioned before we need a mechanism to mimic map and grep for
>> XSUBs (like UNDERBAR, but that works) and for subs. What we don't need
>> is to throw the baby away even before having tried to put water in the
>> tub.
>
> So you are saying that the above will eventually be correct if and only
> if it is instead written like so?
>
>     catch sub (_) {
>         die $_ unless /Can't locate .* in \@INC/;
>     }
>
> How is that less ugly than the use of `local $_`? Is inventing a one-off
> selective lexical-to-dynamic scope connection mechanism for exactly one
> single variable in all of Perl reasonable, modern, nice language design?
>
> [Long parenthetical while I try to entertain the idea anyway follows:]
>
> While I don’t know how UNDERBAR is used, I do know that the underscore
> prototype is the wrong interface. I have written subs that operate on $_
> in addition to stuff they expect in @_ with good reason. Consider, for
> example, Plack::Middleware::Rewrite.
>
> If you are really going to attempt this, I would rather figure out some
> kind of `upper` declarator to give Perl a built-in PadWalker of sorts.
> Then the above would read
>
>     catch {
>         die upper $_ unless /Can't locate .* in \@INC/;
>     }
>
> which, while still far uglier than the global-topic version to me, would
> at least be a saner interface to modifying the scope of a variable: it
> is honest and explicit about being a new scoping mechanism rather than
> obscuring that fact by pretending to be about arguments and the topic.
>
> But really, I would just rather lexical $_ would go away instead of
> having to invent anything this far-reaching just to make `my $_` work.
>
> Another option, which I am more predisposed to, is to introduce light-
> weight blocks that work the way that blocks in `map` and `grep` do.
>
> Note however that this doesn’t entirely relieve the need for some kind
> of `upper`. So I’d like to see those, and for other reasons, and still
> I would rather just that lexical $_ would go away.
>
> Regards,


Hi,

I'm with Rafael here. However, Jesse brought up valid point of writing
subs in pure perl that behave exactly like perl's grep like builtins.

If we take a look at the following line:

my $_ = "foo"
$C = sub { say "C" if grep { /foo/ } qw(bar) };

grep is a special function with (&@) prototype, but this prototype
doesn't describe it completly. It doesn't tell where to lookup $_ (and
probably other variables) within the code block binded to & in the
protototype.

Look at the following example:

perl -E 'our $x = 10; { my $x = 20; { say $x; our $x; say $x; local $x
= 30; say $x } say $x } say $x'
20
10
30
20
10

From my point of view grep is preprocessed into the following code and compiled:

my $_ = "foo"
$C = sub {
  say "C" if do {
    our $_; local $_;
    my @res;
    foreach my $v ( qw(bar) ) {
      $_ = $v;
      push @res, $v if (sub { /foo/ })->()
    }
    return @res;
  }
};

Eg. grep itself decides that within the block $_ will be binded to
global $_, localized and assigned on each iteration. I don't see
reasons why grep-like functions should stick to "our $_; local $_;".
Some may want to do "my $_;" and some may leave $_ alone and let $_
behave like any other variable making the block a closure.

Now that I've said all of the above. A solution would be to extend
prototyped functions to let them explicitly define where $_ points
within the block that is passed, for example (just one possible
syntax):

sub grep_like(&@) :our($_) {...}

The other important difference between grep and grep like pure perl
functions is that pure perl functions can take sub reference while
grep can not (sort can and it's another incosistency). For example:

sub foo(&@) { ... }; say foo sub { say $_; $_[0] ne "foo" }, qw(foo bar);

This means that a function can be defined far away from its use with
call of a prototyped function. In an ideal world (my ideal world) a
sub that closes over some $_ far away gets different $_ when passed by
reference into `sub foo(&@) :our($_) {...}`. For example:

sub foo(&@) :our($_) { my $c = shift; $c->() foreach @_ }

my $_ = 10;
my $sub = sub { say $_ if $_ <= 3 };
$sub->(); # does nothing
foo $sub, 1..10; # says 1, 2, 3

Clearly there are hundreds of yaks to shave. I think it can open door
to more consistent builtins that take code blocks and how variables
are scoped within those blocks. Also, it may open doors to passing sub
references into grep and map rather than just block or a statement.
This opens door to giving the same power to builtins as to pure perl
variants.

No idea how hard it would be to implement.

> --
> Aristotle Pagaltzis // <http://plasmasturm.org/>



-- 
Best regards, Ruslan.

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About