Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: [PATCH 1/3]: Add named references



On Fri, Feb 10, 2023 at 11:45 PM Bart Schaefer
<schaefer@xxxxxxxxxxxxxxxx> wrote:
>
> On Fri, Feb 10, 2023 at 11:02 PM Oliver Kiddle <opk@xxxxxxx> wrote:
> >
> > I'd be
> > fine with the reference becoming unset at the time of the return if it
> > refers to a variable that is going out of scope. Can that be done as
> > part of the same code that drops the local variables?

That's actually a little startling given dynamic scoping.  It leads to this:

{
  typeset -n ref=var
  () {
    var=FUNC1
    typeset -p ref
  }
  typeset -p ref
}

Output:
typeset -g -n ref=var
typeset: no such variable: ref

If the called function were a blackbox, it would be extremely puzzling
why "ref" became unset.

The additional complication is that the parameter table is a hash, so
it's scanned in random order; we can't be sure that the referent local
$var will be found and unset before the referring $ref is examined.  I
suppose we could scan twice (ick).

Instead let's think for a moment about how the dynamic scoping works.
"ref" has two properties:  The locallevel at which it was declared,
and the locallevel at which it prefers to find a referent.  Any time
you use $ref (or assign to ref) the search starts at the current
locallevel and proceeds upward until either the preferred level is
reached or the "topmost" defined parameter is found, whichever comes
first.

So ... if endparamscope() walks the preferred referent locallevel
upward as the scope unwinds, we get (using your earlier example with
my addition):

{
  var=hello
  typeset -n ref
  () {
    typeset var=x
    ref=var
    echo $ref
  }
  typeset -p ref
  echo $ref
  () {
   typeset var=y
   echo $ref
   () {
     typeset var=z
     echo $ref
   }
  }
}

Output:
x
typeset -n ref=var
hello
hello
hello

The inner assignment to ref changes the scope , but it then unwinds so
in the second function (which does not assign to ref) it is back to
pointing at the original scope, and finds $var there.

If "var=hello" is replaced by "unset var" (so there is no parameter at
the original scope), then the output is:

x
typeset -n ref=var

y
y

This is because the assignment "ref=var" has initialized the name to
which "ref" points, but there is no such name at the same level, so
the nearest parameter of the same name is chosen.

If "typeset -n ref" at the top level is replaced by "typeset -n
ref=var" then the "ref=var" assignment in the first function changes
the value of either the global $var or the local $var from "x" to
"var" (depending on which existed first) because ref already has a
referent name.  So the output could be either:

# with var=hello
var
typeset -n ref=var
var
var
var

# with var unset
var
typeset -n ref=var

y
y

Given the requirements that (1) assigning a value to a nameref that
has no referent initializes the nameref (required for "for" loop) and
(2) dynamic scoping selects the best referent by name, I think this is
the best I can do without something even more surprising.

The other option that occurs to me is for the assignment ref=var to
automatically create a local $ref (as if "typeset ref=var" had been
used).  That would then be unwound and we'd end up back in the
original state.  But that would also be unexpected behavior in dynamic
scopes.




Messages sorted by: Reverse Date, Date, Thread, Author