Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Re: Up-scope named references, vs. ksh
- X-seq: zsh-workers 52564
- From: Stephane Chazelas <stephane@xxxxxxxxxxxx>
- To: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>
- Cc: Zsh hackers list <zsh-workers@xxxxxxx>
- Subject: Re: Up-scope named references, vs. ksh
- Date: Tue, 20 Feb 2024 21:05:53 +0000
- Archived-at: <https://zsh.org/workers/52564>
- In-reply-to: <CAH+w=7akb334QvsofyMLtc7_091bmP=omjAGOZSc8eH8FWuTqQ@mail.gmail.com>
- List-id: <zsh-workers.zsh.org>
- Mail-followup-to: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>, Zsh hackers list <zsh-workers@xxxxxxx>
- References: <12608-1675903622.800470@Xj82.e3y1.svhG> <CAH+w=7ZZUCqYe6w1ZqZZKR6iLsZH0SDDXyzwgTU93nxx6bmxjQ@mail.gmail.com> <66045-1675975796.128039@FBF_.0yMO.Y8fk> <CAH+w=7bcqc8SsRxsht0QFyXy=DYzj6nVaBFhdzQ5MrBB+yBz+A@mail.gmail.com> <CAH+w=7YVJO-HkneMpnfBbqBztPaXdXTD=mo-vHbdUW00TiFVBQ@mail.gmail.com> <40726-1676098925.110777@U2kb.d0Pd.I9ml> <CAH+w=7aKwKhdXjPo2FQG1GqjHQzX=5to_m6kZeL-UFfQh_XMtw@mail.gmail.com> <20240211070042.4j37hkgjjn3dfjqd@chazelas.org> <CAH+w=7ateaqX5azdifTaFpJT6sX-fVhnEazgeYYXSWtJY8EQTw@mail.gmail.com> <CAH+w=7akb334QvsofyMLtc7_091bmP=omjAGOZSc8eH8FWuTqQ@mail.gmail.com>
2024-02-17 19:26:07 -0800, Bart Schaefer:
[...]
> Here's what I'm concerned about:
>
> 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 the subtle difference of separating the declaration of "ref" from
> assignment to it.
I wouldn't mind for the typeset -n var=value form to be
required like in mksh, like for typeset -r var=value.
Note that the ksh93 man page mentions:
ksh93> A nameref provides a convenient way to refer to the variable inside a
ksh93> function whose name is passed as an argument to a function. For exam‐
ksh93> ple, if the name of a variable is passed as the first argument to a
ksh93> function, the command typeset -n var=$1 (a.k.a. nameref var=$1) inside
ksh93> the function causes references and assignments to var to be references
ksh93> and assignments to the variable whose name has been passed to the func‐
ksh93> tion. Note that, for this to work, the positional parameter must be
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ksh93> assigned directly to the nameref as part of the declaration command, as
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ksh93> in the example above; only that idiom can allow one function to access
ksh93> a local variable of another. For instance, typeset -n var; var=$1
ksh93> won't cross that barrier, nor will typeset foo=$1; typeset -n var=foo.
> B) Src/zsh -c 'function f { typeset -n ref; typeset var=foo; ref=$1;
> ref=X; echo "$ref ${(!)ref} $var"; }; f var; echo "$var"'
>
> Now the assignment is after the declaration of the local. And finally:
>
> C) Src/zsh -c 'function f { typeset var=foo; typeset -n ref=$1;
> ref=X; echo "$ref ${(!)ref} $var"; }; f var; echo "$var"'
>
> Now the declaration+assignment of the nameref is after the local.
The doc already says:
zsh> A named parameter declared with the '-n' option to any of the 'typeset'
zsh> commands becomes a reference to a parameter in scope at the time of
zsh> assignment to the named reference, which may be at a different call
zsh> level than the declaring function. For this reason, it is good practice
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
zsh> to declare a named reference as soon as the referent parameter is in
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
zsh> scope, and as early as possible in the function if the reference is to a
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
zsh> parameter in a calling scope.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Which I'd think covers that.
> At least for the versions I have installed on Ubuntu 20.04, and with
> parens removed from (!) as appropriate,
Note that you don't want to use that one. IIRC, the one on
Ubuntu 20.04 is based on the now discontinued ksh2020 which was
based on the ksh93v- beta release from AT&T. It was deemed too
buggy and abandoned.
You can find a maintained version based on ksh93u+ at
https://github.com/ksh93/ksh which includes hundreds of bug
fixes (including from Solaris, RedHat and some backported from
ksh2020).
[...]
> So in part C, ksh "knows" that $1 is not just a string but an object
> that belongs to a surrounding scope.
Note that ksh93 is special in that it does static scoping.
There's not a stack of scopes like in shells that do dynamic
scoping (ksh88, zsh, bash...), just a global scope and the
local scope of the function. So having namerefs there is
critical as it's the only way for a function to be able to
access the variables of its caller (though one can also export a
local variable to make it available to callees).
I suppose zsh's private variables are similar to that.
[...]
> 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.
Contrary to mksh or bash, it alread gets this right:
$ ./Src/zsh -c 'function f { typeset -n v=$1; typeset l=foo; v=new; echo "inner l=$l";}; l=before; f l; echo "outer l=$l"'
inner l=foo
outer l=new
$ ksh -c 'function f { typeset -n v=$1; typeset l=foo; v=new; echo "inner l=$l";}; l=before; f l; echo "outer l=$l"'
inner l=foo
outer l=new
$ mksh -c 'function f { typeset -n v=$1; typeset l=foo; v=new; echo "inner l=$l";}; l=before; f l; echo "outer l=$l"'
inner l=new
outer l=before
$ bash -c 'function f { typeset -n v=$1; typeset l=foo; v=new; echo "inner l=$l";}; l=before; f l; echo "outer l=$l"'
inner l=new
outer l=before
The only remaining problem is when the refered variable
is not set/declared.
$ ksh -c 'function f { typeset -n v=$1; typeset l=foo; v=new; echo "inner l=$l";}; f l; echo "outer l=$l"'
inner l=foo
outer l=new
$ ./Src/zsh -c 'function f { typeset -n v=$1; typeset l=foo; v=new; echo "inner l=$l";}; f l; echo "outer l=$l"'
inner l=new
outer l=
$ mksh -c 'function f { typeset -n v=$1; typeset l=foo; v=new; echo "inner l=$l";}; f l; echo "outer l=$l"'
inner l=new
outer l=
$ bash -c 'function f { typeset -n v=$1; typeset l=foo; v=new; echo "inner l=$l";}; f l; echo "outer l=$l"'
inner l=new
outer l=
[...]
> 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 to each of the words in $2, $3, ... ? (E.g.,
> suppose $3 is "var" instead of $1.) Does it change if I do
I find that special behaviour of "for" quite surprising.
I wasn't aware ksh93 and bash did it as well. I find
mksh's behaviour more consistent though I can see how it can be
difficult to do without.
$ 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".
Seems buggy in ksh93:
$ ksh -c 'a=1 b=2; typeset -n v; for v in a b; do echo "v=$v a=$a b=$b"; done'
v=1 a=1 b=2
v=1 a=1 b=2
$ ksh -c 'a=1 b=2; typeset -n v; for v in a b; do echo "v=$v a=$a b=$b ${!v}"; done'
v=1 a=1 b=2 a
v=2 a=1 b=2 b
I would think that would be nowhere as common a usage as the
ones where a function is meant to return something into the
variable(s) passed as argument.
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:
print() {
local _print_{opt,raw=false}
while getopts rv: _print_opt; do
case $_print_opt in
(r) _print_raw=true;;
(v) typeset -n result=$OPTARG;;
esac
done
}
To avoid clashes (or the equivalent with .print.opt instead
thouh I'm not sure I see the benefit if that means one needs to
use ${.print.opt} instead of just $_print_opt).
But that would already be better than what you get in bash or
mksh.
--
Stephane
Messages sorted by:
Reverse Date,
Date,
Thread,
Author