From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-3.2 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_EF,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 Received: from zero.zsh.org (zero.zsh.org [IPv6:2a02:898:31:0:48:4558:7a:7368]) by inbox.vuxu.org (Postfix) with ESMTP id E2CE324199 for ; Tue, 20 Feb 2024 22:06:17 +0100 (CET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=zsh.org; s=rsa-20210803; t=1708463177; b=hQSaF6f3IaY4DzNqF0Rg07oq0i4PqFJNeMWHq9bBXBETToV5gEmG1DuHitre7AxoPg3nFETg32 JAE/bLu7dM5QaUgNPkVfb8bG5Q6/gwV7pUYCLopuHG4Qses6iujkQ+UMAblmZWTa9PwyhIPXsf yOhTogr/o3bBlmKaaheO/8GSOow9Oo61tDUBSLxoBrAwyhK0obN3Plz/6J5+Yp4JGJYHJvzSeq jZRFwF2aaZiqhnpHe6tp3iYLwtL078qiaS7puvETj6ComGy4bvRBCq4eBo1cRV5j3jmIR99dsq O9apnPdqh/JNq8ezMwPuJam7QSf4TuSbfjN57cES/MnmHw==; ARC-Authentication-Results: i=1; zsh.org; iprev=pass (relay6-d.mail.gandi.net) smtp.remote-ip=217.70.183.198; dmarc=none header.from=chazelas.org; arc=none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed; d=zsh.org; s=rsa-20210803; t=1708463177; bh=dLB7i710vUGto3GpDgmP0qkNZ5Q8F3d5vA8tPbK82WQ=; h=List-Archive:List-Owner:List-Post:List-Unsubscribe:List-Subscribe:List-Help: List-Id:Sender:In-Reply-To:Content-Transfer-Encoding:Content-Type: MIME-Version:References:Message-ID:Subject:Cc:To:From:Date:DKIM-Signature; b=Jwe6UWuOfhh2OJP/GvoT0+ksmCpfggXUlYzwuuEdzzSzep5dh/UXTuHX6Cli1pke+D6gczf107 dxttAQLknrXuEc9q4uWxolLNNitY99JQDEzlEur3V4Vozkkei1XLNQAyQOOR7wYCTIGWrz0Kh0 cnvKm0dzff78qadQtcfwyYdy1rgX8TSHRfojptG84rktIQdBbDN7JZwAVjMhQr0CGDnn+qYco4 u1THkpaVOxTXMdynZRncxBIYlLDAVg7uu2pbVVGYOq3hEAxtvkiyUBYfIICGUiXOHn/5UvedEB RWricdZtiD7icN/Nt5FTt6Bt12c8cxDZKl9dw4/ZPUldeA==; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=zsh.org; s=rsa-20210803; h=List-Archive:List-Owner:List-Post:List-Unsubscribe: List-Subscribe:List-Help:List-Id:Sender:In-Reply-To:Content-Transfer-Encoding :Content-Type:MIME-Version:References:Message-ID:Subject:Cc:To:From:Date: Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender :Resent-To:Resent-Cc:Resent-Message-ID; bh=LMw0JSIiqGD26vrx2IFE9GdoK5FEsZE0UU4DJk2c56Q=; b=FqKAaI7P4ZIOT6cZZpnGc4Y3yR jtI69iTKrNEpq8XIMupgds40H6l+HN4I7+7Dwkkpd37sTc7rs+DTy/7qzRUVkZD8b1XV/Q8CjDD78 Q3sIzTSXpgR/vMeqIDT2w/vGXRNmp2ryHNcWQko34q8Tv1O4kSpakTPvwKEApq0Z5MMJ5KjnTB2QN oLFLqGUdsWuxBLnS3X5zYA7LwmBCOlyhmDXfB0EO6Gij+MvJWElR3kV/njuytDZLI1CT86ktimYIf cuTlSZVebabgigc7tCYsIhWyfEkhCN8vRFrfWOP2fgQVBijOlQhlmgTropF2mLJ7A45tJRZULp5Od 6doL7HkA==; Received: by zero.zsh.org with local id 1rcXJf-0007bz-AL; Tue, 20 Feb 2024 21:06:15 +0000 Authentication-Results: zsh.org; iprev=pass (relay6-d.mail.gandi.net) smtp.remote-ip=217.70.183.198; dmarc=none header.from=chazelas.org; arc=none Received: from relay6-d.mail.gandi.net ([217.70.183.198]:45763) by zero.zsh.org with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) id 1rcXJL-0007Kh-Ue; Tue, 20 Feb 2024 21:05:56 +0000 Received: by mail.gandi.net (Postfix) with ESMTPSA id 774E6C0006; Tue, 20 Feb 2024 21:05:54 +0000 (UTC) Date: Tue, 20 Feb 2024 21:05:53 +0000 From: Stephane Chazelas To: Bart Schaefer Cc: Zsh hackers list Subject: Re: Up-scope named references, vs. ksh Message-ID: <20240220210553.g6imt3op6zahz4pa@chazelas.org> Mail-Followup-To: Bart Schaefer , Zsh hackers list References: <12608-1675903622.800470@Xj82.e3y1.svhG> <66045-1675975796.128039@FBF_.0yMO.Y8fk> <40726-1676098925.110777@U2kb.d0Pd.I9ml> <20240211070042.4j37hkgjjn3dfjqd@chazelas.org> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: X-GND-Sasl: stephane@chazelas.org X-Seq: 52564 Archived-At: X-Loop: zsh-workers@zsh.org Errors-To: zsh-workers-owner@zsh.org Precedence: list Precedence: bulk Sender: zsh-workers-request@zsh.org X-no-archive: yes List-Id: List-Help: , List-Subscribe: , List-Unsubscribe: , List-Post: List-Owner: List-Archive: 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