I'll later start a separate thread to discuss support for generalized upper scope references as that is orthogonal to support for references to nested variables.
f() {
local -n ref
local var=${ local X=1 Y=2; for ref in X Y; do ... done }
}
I don't find this example very convincing because it looks too specific. Consider the following example
() {
() { local -n ref; local A=1; ref=A; echo "ref=$ref " }
() { local -n ref; local X=1; ref=X; echo "ref=$ref " }
}
Here too, one may want to factor out "local -n ref" but that won't work. In the first example it only works because the "for" construct is used. If that's the only reason we should support references to nested variables, it sounds like a very high price for a very specific and most likely very rare use case.
I think that a more convincing argument is what Oliver alludes to, namely that average users may simply expect that reference to nested variables work. On one hand we have advanced users that understand that they should use "typeset -nu" rather than a plain "typeset -n" to refer to variables passed by name. Expecting from these users to understand that the references are relative to the declaration scope of the reference is probably not asking too much. On the other hand, we have average users who kind of just understand the concept of named reference but have no knowledge of all the intricacies. For them, it might be surprising that initializing an encolsing referece with a nested variable doesn't work.
If that's our concern, then I guess references to nested variables should be supported.
Now, regarding all the way up, since my previous arguments couldn't convince you, here is one of your own, namely the lack of self-reference detection.
in fact, because (with your "loose" experiment patch) a
reference can "float" back into its own scope, it's trivially easy to
cause a reference to loop back to itself.
What example did you have in mind? I noticed that in the following example the loop is detected only if the outer ref1 is commented out.
() {
typeset ref1=outer
() {
typeset -n ref2=ref1
echo "ref1=$ref1 ref2=$ref2"
typeset -n ref1=ref2
echo "ref1=$ref1 ref2=$ref2"
}
}
It's a little surprinsing to me. I expected that the second ref1 definition would detect the loop since all the information needed to do so is presnt at that point of time.
Anyway, just an aside, my point is that you can also easily create loops with the current implementation:
() {
typeset -n ref1=ref2
typeset -n ref2
() {
() {
typeset ref1=inner;
ref2=ref1
echo "ref1=$ref1 ref2=$ref2"
}
echo "ref1=$ref1 ref2=$ref2"
}
}
If desired, the loop created when the innermost scope is exited could be detected. It would require to lookup what ref2 newly refers to after its previous referent ceased to exit. If that is done then all the way up comes for free.
typeset -nu always being relative to declaration does seem to be the
most logical, sensible and useful behaviour. I believe this is what has
now been implemented and pushed.
Indeed, in the latest implementation "typeset -nu ref; ...; ref=var" searches for "var" in the enclosing scope of where "ref" was defined, independent of the scope where "ref=var" is called.
Fixing typeset -n to the current scope (applying the -u0 Philippe
suggested by default) would seem counterintuitive to me. With the
default of dynamic scoping, I'm used to being able to just use variables
from parent scope. Digging into parent scopes should always be very much
explicit.
You would still be able to use variables from the parent scope. What -u0 means is that the serach for "var" doesn't start in the scope where "ref=var" is called but in the scope where "typeref -n ref" is defined but the search still looks into enclosing scopes. The following example would print "ref=1" if -n defaulted to -nu0.
() {
typeset var=1
() {
typeset -n ref
() {
typeset var=2
ref=var
echo ref=$ref
}
}
}
Philippe