Path: utzoo!utgpu!watserv1!watmath!att!rutgers!usc!elroy.jpl.nasa.gov!jpl-devvax!lwall From: lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) Newsgroups: comp.lang.perl Subject: Re: call-by-reference, $#_, and local() Message-ID: <8751@jpl-devvax.JPL.NASA.GOV> Date: 16 Jul 90 19:33:27 GMT References: <15096@thorin.cs.unc.edu> Reply-To: lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) Organization: Jet Propulsion Laboratory, Pasadena, CA Lines: 96 In article <15096@thorin.cs.unc.edu> tell@currituck.cs.unc.edu () writes: : I think I may have found two (possibly related) bugs in Perl 3.0 pl 18, or at : least some really subtle points. I've been able to reproduce them with the : short programs below, extracted from a much larger program. : I'm trying to pass several arrays to a subroutine by reference so the : subroutine can fill them in. : ----------------------------------------------------------------------------- : Problem 1. : Subroutine next_wire_al reads a record from a file and : attempts to use $#_ (the number of arguments) : to behave somewhat differently in the presence of various arguments. : : Argument 0 is a string that is set by the routine. : Arguments 1, 2, and optionaly 3 are arrays passed by reference with with "*." : Argument 3 can be omitted. : : When the test "if($#_ == 3)" is present, the third argument gets munged as the : routine returns; the @attribs array prints out as empty in the calling : routine. The value of $#_ printed is always correct, and the if() always does : the right thing as evidenced by the print within it. Stepping through with : the debugger suggests that the call-by-reference is not working with the : "if($#_ == 3)" there (@wattribs is not identical to @attribs in this case). : : So, what's going on? I've read the manual several times and don't see : anything special about $#_. There's nothing special about $#_. There is something special about using local() inside a BLOCK. : if($#_ == 3) { : local(*wattribs) = $_[3]; : print "getting attributes\n"; : $do_wattribs = 1; : } : # [intermediate code omitted] : if($do_wattribs) { : @wattribs = grep(/\w*=\w*/, @allattribs); : if ($#wattribs>=0) { : print "attribs = ",join(';', @wattribs), "\n"; : } : } That local there means that *wattribs is local to your conditional. It's out of scope by the time you try to write to it. : Problem 2: : In this shorter bit of code, the local(@at) call in get_at appears to : hide @at in routine one, even though we've passed a reference to it. : If this behavior is correct, it seems really counterintuitive. I would : think that taking *at in the call to routine get_at would put the "pointer" : to the original array into $_[0], and that later calling local(@at) should : only hide that original array @at when it is accessed by name (creating a : new @at, but not changing where the "pointer" from the * operator points.) When you pass in *at to your subroutine, it's passing a reference to the symbol table entry for everything named "at". You then say local(@at), which replaces a specific pointer within that symbol table entry. Then you use $_[0] to make a local symbol table entry, but the entry being referenced has already been modified by the previous local. The basic problem is the semantics of local, which currently just replaces the global pointer temporarily, saving the real global pointer in the meantime. The interaction of dynamic scoping and call-by-reference sometimes is counter-intuitive, as you say. To fix this would require making a local symbol table entry for every localized variable, not just for *variables. I'm looking at ways to do that logically without imposing too much of a time penalty, but it's not a trivial problem. One workaround is to use packages where appropriate to put service routines into a different namespace than the client routines. This doesn't help if you call yourself recursively, but in those cases you can just pick your variable names carefully. : While I've got everybody's attention (especially Larry's) is there any chance : that perl -d will ever be usable on 'do' files (library routines, for : example)? How about hooks so one can look at the arguments to a subroutine- : glancing through perldb, I understand why with the current debugger : implentation $_[0] gives me the line number and not what I expect at the point : where the program is stopped, but it is really confusing at times. Someday the debugger will be useful in "eval" contexts, but it's not on the top of my list. That'd be a good project for someone masochistic enough to enjoy looking at the innards of Perl. Please talk to me before you try it, however. A mod to allow looking at a local copy of @_ directly wouldn't be difficult-- just copy @args back into @_ at the appropriate point in sub DB--perhaps only if the current command contains an underline. Hmm, you might have to do it all the time if a break or trace command referred to @_. Rather more work would be to allow mods to @_ to be copied back into the real @_. This is close to impossible without mods to C code, I think. Larry