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

Re: All the way up or current scope



Ha, this makes me think that if "all the way up is adopted", an equivalent but most certainly easier to understand specification than the one that I have proposed in Reverse Engineering Zsh Named References is possible.

At any point in time, if ref=var, then
- if var was locally defined in the current scope before ref was initialized, then ref refers to it,
- otherwise, ref refers to the first (strictly) enclosing var, if any and otherwise to the local var, if any.

For named references defined with -u, if -u is a property of the reference, then ideally the specification should be as follows:

At any point in time, if ref=var and ref was defined with -u, then
- ref refers to the first (strictly) enclosing var, if any and otherwise refers to the local var, if any.

If instead -u is a property of the referent, then ideally the specification should be as follows:

At any point in time, if ref=var and ref was defined with -u, then
- consider the var picked, if any, when ref was initialized. If that var still exists, then ref refers to it. Otherwise, ref refers to the same var as if ref had been defined without -u.
- When ref is initialized, the first (strictly) enclosing var is picked, if any and otherwise the local var, if any.

I have the impression that "all the way up" with no consideration of whether -u was used leads naturally to the second option for -u  (property of the referent) but I will have to double check that with some examples. It's not yet clear to me whether a modified version of "all the way up" could lead to the first option for -u (property of the reference). If yes, then we could choose whichever we prefer. I have a preference for the first one as it's simpler and doesn't depend on details of what happened before.

Philippe


On Sat, May 10, 2025 at 3:35 PM Philippe Altherr <philippe.altherr@xxxxxxxxx> wrote:
[…] you only need to know with what variable name ref is initialized and where variables with that name currently exist. 

Actually this is not entirely true. If a variable var exists in the current scope you also need to know whether ref was initialized to var before or after var was defined in the current scope. It remains true that apart from that you don't need to know any of the implementation details of the functions called so far and in particular whether and when they defined variables named var.

Philippe


On Sat, May 10, 2025 at 3:19 PM Philippe Altherr <philippe.altherr@xxxxxxxxx> wrote:
When a function is exited and the scope of named reference is updated because it referred to a local variable of the exited function, should its scope be set to the (new) current scope (i.e., the scope of the calling function) or should it be set all the way up to the scope of the next enclosing rname variable or the global scope if no such variable exists?

The current implementation does the former. I think that the latter would be better.

Consider the following function:

function f1() {
  typeset args=($@);
  typeset -n ref;
  typeset var=$0;
  function f2() {
    function f3() {
      if (($args[(I)f3])); then typeset var=$0; fi;
      ref=var;
      echo "$0: ref=$ref";
    }
    f3;
    echo "$0: ref=$ref";
    if (($args[(I)g2])); then typeset var=$0; fi;
    echo "$0: ref=$ref";
    function g3() {
      typeset var=$0;
      echo "$0: ref=$ref";
    }
    g3;
    echo "$0: ref=$ref";
  }
  f2;
}


Below are the results for 4 different calls.

|--------------------------+--------------------------|
| No var defined in f3     | Local var defined in f3  |
|--------------------------+--------------------------|
| % f1                     | % f1 f3                  |
| f3: ref=f1               | f3: ref=f3               |
| f2: ref=f1               | f2: ref=f1               |
| f2: ref=f1               | f2: ref=f1               |
| g3: ref=f1               | g3: ref=g3               |
| f2: ref=f1               | f2: ref=f1               |
|--------------------------+--------------------------|
| % f1 g2                  | % f1 f3 g2               |
| f3: ref=f1               | f3: ref=f3               |
| f2: ref=f1               | f2: ref=f1               |
| f2: ref=f1               | f2: ref=f2               |
| g3: ref=f1               | g3: ref=f2               |
| f2: ref=f1               | f2: ref=f2               |
|--------------------------+--------------------------|


Even though the only difference between the calls in the two columns is that a local variable var is additionally defined in f3 in the calls in the second column, it changes how ref behaves in f2 and g3.

In order to understand how ref behaves in f2 and g3 after f3 exited, you have not only to know with which variable name ref is initialized and where variables with that name currently exist but you also have to know whether a variable with that name existed within f3***.

If instead the scope of updated named references was set all the way up, then all lines in the examples above, except for the ones printed by f3, would be the same. This has the benefit that to understand what ref refers to at any point in the program you only need to know with what variable name ref is initialized and where variables with that name currently exist. The circumstances under which ref was initialized and whether other variables with the specified name existed in now closed scopes doesn't matter.

Given this, it seems to me that "all the way up" is preferable to "current scope" because it makes it much easier to understand what a named reference is referring to.

Philippe

*** Actually it's even worse, you also have to know whether the variable var in f3 was defined before or after ref was initialized.



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