zsh-workers
 help / color / mirror / code / Atom feed
From: Stephane Chazelas <stephane@chazelas.org>
To: Bart Schaefer <schaefer@brasslantern.com>
Cc: Zsh hackers list <zsh-workers@zsh.org>
Subject: Re: Up-scope named references, vs. ksh
Date: Tue, 20 Feb 2024 21:05:53 +0000	[thread overview]
Message-ID: <20240220210553.g6imt3op6zahz4pa@chazelas.org> (raw)
In-Reply-To: <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


  reply	other threads:[~2024-02-20 21:06 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-06  2:21 [PATCH 1/3]: Add named references Bart Schaefer
2023-02-08  3:45 ` Oliver Kiddle
2023-02-08  4:59   ` Bart Schaefer
2023-02-08 23:16     ` Bart Schaefer
2023-02-09  0:47     ` Oliver Kiddle
2023-02-09  2:01       ` Oliver Kiddle
2023-02-09  5:45         ` Bart Schaefer
2023-02-09  4:49       ` Bart Schaefer
2023-02-09 20:49         ` Oliver Kiddle
2023-02-09 23:07           ` Bart Schaefer
2023-02-11  3:04             ` Bart Schaefer
2023-02-11  3:55               ` Bart Schaefer
2023-02-11  5:36                 ` Speaking of dangerous referents Bart Schaefer
2023-02-12  8:00                   ` Oliver Kiddle
2023-02-12  8:34                     ` Bart Schaefer
2023-02-11  7:02               ` [PATCH 1/3]: Add named references Oliver Kiddle
2023-02-11  7:45                 ` Bart Schaefer
2023-02-11 23:43                   ` Bart Schaefer
2023-02-11 23:45                     ` Bart Schaefer
2023-02-12  7:38                     ` Oliver Kiddle
2024-02-11  7:00                   ` Stephane Chazelas
2024-02-11 16:14                     ` Bart Schaefer
2024-02-11 16:42                       ` Bart Schaefer
2024-02-18  3:26                       ` Up-scope named references, vs. ksh Bart Schaefer
2024-02-20 21:05                         ` Stephane Chazelas [this message]
2024-02-20 22:30                           ` Bart Schaefer
2024-02-21 20:12                             ` Stephane Chazelas
2024-02-29  5:16                               ` Bart Schaefer
2024-03-01 18:22                                 ` Stephane Chazelas
2024-03-01 20:34                                   ` Bart Schaefer
2024-03-02  7:29                                     ` Bart Schaefer
2024-03-02 23:55                                       ` [PATCH] "typeset -nu" (was Re: Up-scope named references, vs. ksh) Bart Schaefer
2024-03-01 23:28                                   ` Up-scope named references, vs. ksh Bart Schaefer
2024-03-03 13:44                                     ` Stephane Chazelas
2024-03-03 19:04                                       ` Bart Schaefer
2024-03-03 20:27                                         ` Stephane Chazelas
2024-03-03 22:58                                           ` Bart Schaefer
2024-03-04 19:59                                             ` Stephane Chazelas
2024-03-05  1:05                                             ` Oliver Kiddle
2024-03-05  2:53                                               ` Bart Schaefer
2024-03-05  5:43                                                 ` Mikael Magnusson
2024-03-05  6:30                                                   ` Stephane Chazelas
2024-03-06  5:04                                                     ` [PATCH] local vs. nameref scoping (was Re: Up-scope named references, vs. ksh) Bart Schaefer
2023-02-12  9:02             ` [PATCH 1/3]: Add named references Oliver Kiddle
2023-02-12 18:59               ` Bart Schaefer
2023-02-12 19:45                 ` PM_* flags in zsh.h (was Re: [PATCH 1/3]: Add named references) Bart Schaefer
2023-02-12 21:01                   ` Oliver Kiddle
2023-02-12 22:54                 ` [PATCH 1/3]: Add named references Oliver Kiddle

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240220210553.g6imt3op6zahz4pa@chazelas.org \
    --to=stephane@chazelas.org \
    --cc=schaefer@brasslantern.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).