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

Re: All the way up or current scope



Great! I have also seen your patch for -nu. Thanks for that! I haven't tested it yet but I will do it soon. I take from your answers that you are not fundamentally opposed to making regular references behave as my proposed -u0 references if both are compatible with ksh and if the changes in zsh are only observed in corner cases or in cases that amount to bad programming "style". Adopting -u0 references as the default would have the benefit of a much simpler implementation. All our discussions about whether to adopt "all the way up", how to fix dangling references, and how to deal with chains of references that can lead to hidden ones would become moot.

My current guess is that in most cases where named references are used as intended in a good programming style, -u0 and our current default references behave more or less the same and are both compatible with ksh where that is possible. However, that's currently only a guess. I'm thinking of creating a doc with a side-by-side comparison of ksh, with static and dynamic scopes, and zsh, with the current references, -u0 references, and -u1 references. This will show where the differences actually are and hopefully will help us decide whether adopting -u0 references is an option.

One thing that would help me for creating that side-by-side comparisons is the ability to test -u0 references with a real implementation rather than simulating them in my head. Thanks to your patch for -u1 references, it shouldn't be difficult for me to turn it into one that works for -u0. However, what would be nice is that I can actually select the desired behavior in the source code via an argument to -u, which for now could as well be mandatory. It would probably take me too much time to figure out how to parse that argument and pipe it into the right Param instance. I would rather default to a hack, like relying on a global var (ULEVEL=0 zsh ...). If you readily know how to implement such an argument and pipe it through into some field of Param and it only takes you a few minutes, a patch doing it would be very much appreciated. No need to update Param.c to use that argument or to fix any test broken by a mandary -u argument. If such a patch would take you more than a few minutes, then I can as well spend a few minutes adding my little hack.

I don't think the second part of that can work exactly as stated.  I
don't really follow the conditions in which assigning would have no
effect.

Here is the only case I'm aware of where -u won't be able to do what we would like it to do:

function f() {
  typeref -nu ref=$0;
  typeref var2=f;
  ref=result;
  echo "f: var2=$var2";
}
function g() {
  f var1
  echo "g: var1=$var1";
  f var2;
  echo "g: var2=$var2";
}

The desired output is the following one:
f: var2=f;
g: var1=result;
f: var2=f;
g: var2=result;

The second call to f with var2 is problematic. When ref is assigned in f, there is no var2 to be found in the enclosing scope. A global variable should therefore be defined, like in the first call to f with var1, but that's not possible because a variable var2 already exists in the current scope of f. It would require the ability to do something that not even "typeref -g" can currently achieve. My impression is that technically it could probably be achieved but since this breaks a very long-standing invariant, namely that new variables with the same name as an already existing one can only ever be defined in a more deeply nested scope than the scope of the already existing variable, I wouldn't change that. Especially not for a case of what could be deemed as bad programming style. Indeed, the caller g has no excuse for not first defining var2 either with "local var2" (or "local var2; unset var2" if being initially unset is important) before passing var2 by name.

In this case, the implementation should refuse to perform the assignment. In particular, it should NOT default to assign f's var2 as that's not at all the behavior that was intended. It may print an error message, which would certainly be useful to help the implementers of g understand what's going wrong.

Expected output:
f: var2=f;
g: var1=result;
f:3: error: a named reference that refers to a not-yet-defined variable var2 in an enclosing scope is being assigned from a scope where the would be enclosing variable var2 is hidden by a local variable var2. Please make sure that var2 is defined before passing it by name or before using its name to initialize a named reference.
f: var2=f;
g: var2=;

We can certainly come up with a more concise error message :-)

It's getting late at night here so I'm going to call it a day and
think through your "Indeed" message sometime tomorrow.

Sorry if that "Indeed" sounded offensive. I just ran the example with 5.0.9-test-2 and noticed that it did indeed behave as expected. Which was a little surprising as from what I remember -u references seemed to behave in bogus ways in most contexts. But then it could also be just by chance. Or did 5.0.9-test-2 actually more or less behave as my proposed -u references and I failed to see it? If that's the case, I'm very sorry. I will have an eye on that too when I do my side-by -side comparison.

And now, time for me to do something else. I spent way too much time on these issues in the past days. I sorely need some change. Maybe I can get the side-by-side comparison done by tomorrow. If not, by Saturday. Thursday, I will embark on a ferry for almost 2 days. That should give me plenty of time to finish it.

Philippe


On Tue, May 13, 2025 at 7:11 AM Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
On Mon, May 12, 2025 at 11:42 AM Philippe Altherr
<philippe.altherr@xxxxxxxxx> wrote:
>
> At any point in time, if ref=var and ref was defined with -u, then
> - if var refers to a variable in the enclosing scope of the scope where ref was defined, then expanding ref returns the expansion of that variable and assigning to ref assigns to that variable,
> - otherwise, expanding ref returns the empty string and assigning to ref first defines a global variable var and then assigns to it unless a variable var already exists (in the current or an enclosing scope), in which case assigning to ref has no effect.

I don't think the second part of that can work exactly as stated.  I
don't really follow the conditions in which assigning would have no
effect.

In any case after workers/53602 the behavior should be very close to that.

It's getting late at night here so I'm going to call it a day and
think through your "Indeed" message sometime tomorrow.


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