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

Re: [perl #120047] perl should enable "$_" for use before callingsubs

Thread Previous | Thread Next
From:
Lukas Mai
Date:
September 30, 2013 01:08
Subject:
Re: [perl #120047] perl should enable "$_" for use before callingsubs
Message ID:
5248CF01.7070506@gmail.com
On 30.09.2013 02:17, Linda Walsh via RT wrote:
> On Sun Sep 29 13:24:28 2013, plokinom@gmail.com wrote:
>> But regarding library code: If your library code blindly modifies global
>> variables (such as $_), it's probably broken.
> ---
> It didn't "blindly modify" it.  The value of $_ was *saved*
> in the code that made use of it and restored when done.
> 
> That would *seem* to be a perfectly reasonable solution in most 
> languages.  Even in assembler, if you use registers, you save them,
> you use them, you restore them.
> 
> Asking for that ability in perl.
> 
> if I have:
> 
> sub reader() {

Don't use prototypes without a good reason.

>   my save=$_";

Syntax error.

>   open(my $fh, "<myfile") or die "open:$!";

Don't use 2-arg open. Mention the filename in the error message.

>   while (<$fh>) {

Don't blindly overwrite globals in subroutines.

>   ...
>   }
>   $_=$save;
> }
> 
> that saves $_ and restores it upon exit -- it will still
> fail if, in the callstack above "somewhere", that called this --
> it was called from a 
> 
> for ('a' 'b' 'c') {

Syntax error.

> ...reader()

Syntax error.

> }
> 
> reader will fail with an error claim it attempted to modify
> a read-only var.
> 
> The library function cannot use while(<>) while "$_" is read
> only -- despite the fact that it was trying to be "good" and
> saved $_ before using it (and restored it upon exit).
> 
> That is the issue.

There are a few problems with this.

1) Even if $_ happens not to be read-only, you're still writing to it.
This makes a difference if the underlying variable is tied, for example:

tie my $var, "Some::Class";
for ($var) {
    reader();  # calls tied($var)->FETCH and tied($var)->STORE
}

If FETCH or STORE have side effects, this can have unintended results.

2) Your restoration doesn't run if the subroutine doesn't reach its last
line, e.g. because it 'return's from the loop body or something throws
an exception or someone decides it would be a good idea to 'goto &foo'
into another sub.

Solution: local $_;

Not only will this automatically restore $_ on scope exit, it also makes
the code work even when called from a 'for ("a", "b", "c")' loop.

3) However, you're still overwriting a global. Imagine the following
call stack:

  foo() --[calls]--> bar() --[calls]--> baz()

If foo() sets a global variable $X and baz() reads it, then baz() will
see any changes to $X made by bar(), even if bar() is careful and
restores the old value before returning to foo().

Solution: Use lexical variables.

Alternatively, define an informal protocol that no subroutine can rely
on $_ having any particular value on entry and make sure all subs follow it.

The analogy with registers in assembler code doesn't quite fit IMHO: You
don't alias registers to other registers, memory locations, or
constants, but $_ gets aliased all the time.
Also, we only save/restore registers in assembler code because there are
so few of them, so they need to be reused. This restriction doesn't
apply to Perl. We can define as many lexical variables as we want and
every sub (block, actually) can have its own!


Finally, I note that you haven't answered my question:
> I don't understand what your original bug report was about. I found it
> too long and confusing and stopped reading halfway through. What
> exactly is your proposed change?

I see no feature proposal above, only a problem description. And I think
the problem description rests on some faulty assumptions.

-- 
Lukas Mai <plokinom@gmail.com>

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