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

Re: Up-scope named references, vs. ksh



On Tue, Feb 20, 2024 at 1:05 PM Stephane Chazelas <stephane@xxxxxxxxxxxx> wrote:
>
> 2024-02-17 19:26:07 -0800, Bart Schaefer:
> [...]
> >  A) Src/zsh -c 'function f { typeset -n ref; ref=$1; typeset var=foo;
> > ref=X; echo "$ref ${(!)ref} $var"; }; f var; echo "$var"'
>
> Note that the ksh93 man page mentions:
>
> ksh93> Note  that,  for this to work, the positional parameter must be
> ksh93> assigned directly to the nameref as part of the declaration command
> ksh93> [...]   For instance, typeset -n var; var=$1
> ksh93> won't cross that barrier

And yet, after replacing ksh2020 with Version AJM 93u+ 2012-08-01 (I
had already done this on a different workstation but had forgotten
about it on the one where I tested):

ksh -c 'function f { typeset -n ref; ref=$1; typeset var=foo;
ref=X; echo "$ref ${!ref} $var"; }; f var; echo "$var"'
X var foo
X

So it sure looks like the barrier was crossed in a case where the doc
stated it would not be.

With 93u+ "typeset -n ref=;" produces
  ksh[2]: f[1]: typeset: : invalid variable name
rather than crashing.

> Note that ksh93 is special in that it does static scoping.
>
> I suppose zsh's private variables are similar to that.

Sort of ... private variables are never accessible in another scope,
even with namerefs.

> [...]
> > Given that it's not possible to fix part C for zsh, and zsh agrees
> > with ksh on part B and with mksh on B and C, is it worth making an
> > effort to fix Stephane's original example along with part A ?
> [...]
>
> Seems to me if we have to work around it by namespacing
> variables or use argv like in functions/regexp-replace, that
> defeats the purpose of those namerefs.

You don't have to use namespacing, you just have to be sure that
either the caller has declared the parameter that it wants referenced,
or that the called function does the "typeset -g $1" preliminary (and
follows the doc recommendation about doing the "typeset -n" early).
There's nothing really to be done about the other cases, and
particularly not about specifying the (dynamic) scope of the referent.

> The only remaining problem is when the refered variable
> is not set/declared.

It IS possible to make that "typeset -g" implicit, but that has other
potential effects such as creating an (unset) parameter in global
scope (triggering WARN_CREATE_GLOBAL?).  If the parameter already has
been declared in a calling function scope, then even with the -g, the
nameref will point to that one, even if it's more than one level up
the call chain.  Which ought to be what you expect from dynamic
scoping.

> Contrary to mksh or bash, it alread gets this right:

Yes, as soon as an existing parameter is found the nameref records
what scope contains that parameter and looks for it there when
(de)referenced.  The glitchy bit is when there is no existing
parameter from which to obtain a scope.  I'm reluctant to try to make
this too magical, which is why I lean toward asking function authors
to explicitly do the "typeset -g $1" when that's what they mean.
Could certainly add this to the doc.

Or maybe add an option to "typeset" to turn this on or off, though I'm
not sure the existing function call framework provides all the needed
info.

> > The potential for confusion here seems large.  If I do
> >
> >  typeset -n up=$1
> >  typeset var=foo
> >  for up in $2 $3 $4 $5; do ...
> >
> > what scope am I applying
>
> I find that special behaviour of "for" quite surprising.

So did I.  I await with gritted teeth the first time that Ray forgets
to declare his loop variable with a nameref of the same name in scope.

Mixing namerefs with dynamic scoping does have a number of potential gotchas.

> I wasn't aware ksh93 and bash did it as well.

If they didn't, I wouldn't have implemented it.

> Seems buggy in ksh93:

Not all that surprising given the potential confusions.

> $ mksh -c 'a=1 b=2; f() { typeset name; for name do typeset -n v=$name; echo "$name=$v"; done; }; f a b'
> a=1
> b=2
>
> Won't work for "f name".

There's probably a way to do it with "f name a b" + shift, but I'm not
going to work it out.

> I can see how it can still be difficult to do without
> namespacing. For instance having a function that does option
> processing à la print -v var would have to do:

zparseopts and/or a declared assoc can get you a long way in this sort
of example while minimizing the opportunity for clashes.




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