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

Re: All the way up or current scope



The ksh documentation states that an important use case of typeref -n is to be able to access variables from the calling function. I was wondering how that could work with static scoping where each function only ever sees variables defined by itself. The trick apparently is that if the named reference is initialized in the typeref statement with a value that contains $1 (or $2, $3, ...) then it refers to a variable of the calling function. Otherwise it refers to a variable of the current function:

function f {
  typeset var=$0;
  echo "$0: var=$var";
  function g {
    typeset var=$0;
    echo "$0: var=$var";
    typeset -n ref1=var$1 ref2=var ref3 ref4;
    ref3=var$1 ref4=var;
    echo "$0: ref1=$ref1 ref2=$ref2 ref3=$ref3 ref4=$ref4";
  }
  g "";
}

% f
f: var=f
g: var=g
g: ref1=f ref2=g ref3=g ref4=g

Note that only ref1 refers to f's var even though all 4 refs are initialized with the same value "var". Note also that it doesn't really matter what $1 expands to. It just has to be present. And defined; if g is called with no argument then ref1 refers to g's var.

Thankfully Zsh doesn't seem to do any of this crazy stuff and hopefully nobody aims to replicate it.

Philippe


On Sat, May 10, 2025 at 8:41 PM Philippe Altherr <philippe.altherr@xxxxxxxxx> wrote:
What version of ksh are you referring to? I have installed Homebrew's ksh93:

% ksh --version
  version         sh (AT&T Research) 93u+m/1.0.10 2024-08-01

In ksh, old style functions (f () { … }) have dynamic scoping and new style functions (function f { … }) have static scoping.

To my great surprise, with dynamic scoping all functions apparently share the same set of variables. In Zsh, when a nested function defines a local variable already defined in an enclosing function it gets its own variable that hides the existing one. In ksh, the already existing variable is apparently simply reused. The following quote from the man page confirms this understanding:

Functions defined with the name() syntax and functions defined with the function name syntax that are invoked with the .
special built-in are executed in the caller's environment and share all variables, options and traps with the caller.


The following function also seems to confirm it:

g1() {
  typeset var=g1;
  typeset -n ref;
  g2() {
    typeset var=g2;
    ref=var;
    echo "g2: var=$var";
    echo "g2: ref=$ref";
  }
  g2;
  echo "g1: var=$var";
  echo "g1: ref=$ref";
}


% g1
g2: var=g2
g2: ref=g2
g1: var=g2
g1: ref=g2

Notice that in g1 var=g2 and not g1. I first thought that in g1 ref was still referring to the now dead var variable from g2 but no, there is simply a single var variable. The typeref in g2 doesn't create a new variable, it just updates the existing one. Apparently with dynamic scoping all ksh typeref statements are equivalent to Zsh's typeref -g statements.

Below is the same function using static scoping:

function h1 {
  typeset var=h1;
  typeset -n ref;
  function h2 {
    typeset var=h2;
    ref=var;
    echo "h2: var=$var";
    echo "h2: ref=$ref";
  }
  h2;
  echo "h1: var=$var";
  echo "h1: ref=$ref";
}


% h1
h2: var=h2
h2: ref=var
h1: var=h1
named-refs.ksh[41]: h1: line 39: ref: no reference name

Here, what happens is that the ref=var in h2 doesn't initialize the ref defined in h1 because that ref isn't visible in h2 due to static scoping. Instead it creates a local string variable named ref, which leads to the output h2: ref=var. Then, in h1, the echo $ref fails because ref isn't initialized.

Let me know if I missed something but if all of this is correct, then all the different cases that we have discussed have no equivalent in ksh and thus "works like ksh" doesn't make much sense for them.

Philippe


On Sat, May 10, 2025 at 5:19 PM Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
On Sat, May 10, 2025 at 7:19 AM 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?

I'll be AFK most of today and don't have time to confirm right now,
but I'm reasonably sure that "all the way to the top" violates the
"works like ksh" condition.


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