The reasoning behind my suggestion (above)
was that inline assignments should always yield something that
actually goes into the environment: the -x flag in the "as if local
-x" is important, and your proposal discards it in the case of
references.
OK, I see your point. If the command is an external command, then if the i
nline assignment doesn't export the assigned value, it has no effect and could as well not be there. So it makes sense to expect that an inline assignment always exports the assigned parameter. My suggestions in workers/54948 is actually based on the same reasoning. If var is a string that isn't already flagged for export, then running "var[n]=val cmd" is equivalent to running just "cmd", even though it looks likely that the user expected the equivalent of "(var[n]=val; export var; cmd)".
Does the current Zsh do what you suggest? Not really. It is true that if "var" is an array then "var=str cmd" turns "var" into a string and exports it but "var+=str cmd" doesn't change the type of "var" and doesn't export anything, same for "var[2]=str cmd". There is nothing in the code that indicates an intention to always export something. Quite on the contrary, there is explicit code to avoid exporting anything new in case the assigned value isn't a string, like in "var=(x) cmd" or if the assigned parameter has a subscript.
It may well be that inline assignments were initially introduced to run external commands with an enhanced/modified environment. However, inline assignments can also be used with user defined functions. In that context, it's not uncommon that the main/only desired effect is the temporary modification of the value of the assigned parameter; the flagging of the assigned parameter for export may not be needed/desired at all.
One case where I found that inline assignments are particularly useful is for complex recursive functions that depend on many parameters and where most recursive calls only need to change a subset of all the parameters. In that case, it's much cleaner to declare all the parameters outside of the function and perform (recursive) calls with inline assignments to modify the parameters that need a different value. This is also much more flexible than to pass all parameters via positional parameters. Indeed, with external parameters, there is no difficulty to define recursive functions that depend on multiple array parameters, while with positional parameters, calls would be way more complex as they would have to encode all the arrays into a single sequence of strings. This way of passing parameters also has the advantage that integer and float parameters don't have to be converted to strings and then back to integers and floats. In that context, whether inline assignments export anything is irrelevant but it's important that they preserve the type of the assigned parameters, which is what the current implementation does (except if you run "arr=str cmd" but that's no different from running just "arr=str").
When inline assignments are used as a mechanism for passing typed parameters in function calls, it becomes interesting to also be able to pass references in the same way. That's exactly what my proposal for inline assignments of references enables. It is true that with my proposal "ref=foo cmd" where "cmd" is an external command becomes equivalent to just "cmd" but on the other hand if cmd is a user defined function, then it allows to call it with an updated reference, which can be very useful.
Could we reconcile both approaches? Actually, yes. The current implementation forbids the flagging of references for export. This could be changed. It would raise the question of what should be exported for a reference "ref". I see only two possibilities that make sense: either "$ref" or "${(!)ref}". The former is technically difficult to implement efficiently while the latter is trivial. It's also what Bash does:
% bash -c 'var=foo; typeset -n -x ref=var; printenv ref'
var
If we adopt the latter, then "ref=val cmd" behaves as you suggest; the environment will be augmented with "ref"->"val". Although, only if "val" is a valid identifier. Otherwise, an error will be triggered. However, that's not very different from "int=foo cmd" where "int" is an integer parameter, which augments the environment with "int"->"0" rather than with "ref"->"foo":
% zsh -c 'typeset -i int; int=foo printenv int'
0
Note that ksh's inline assignments behave exactly as the combined approach described above:
% ksh -c 'function f { var=foo; typeset -n ref=var; ref=bar printenv ref; }; f'
bar% ksh -c 'function f { var=foo; typeset -n ref=var; ref=not-an-id printenv ref; }; f'
ksh: f[1]: printenv: not-an-id: invalid variable name
However, I wonder whether the exporting is a bug rather than a feature because ksh forbids explicit exports of references:
% ksh -c 'function f { var=foo; typeset -n -x ref=var; }; f'
ksh: f[1]: typeset: -n cannot be used with other options except -g
Wdyt? Could we allow flagging references for export such that "typeset -n -x ref=var" exports "ref"->"var" and change "ref=var cmd" to A) flag "ref" with -x and B) assign "ref" (rather than the variable referred to by "ref", if any) with "var" ? Obviously, if we do that, then
workers/54943 should not be committed.
Philippe