Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[Language Design] Should inline assignments create local parameters?



Command inline assignments modify the environment of the command when it is executed. For example, the statement "var=foo cmd" executes "cmd" in an environment where "var" equals "foo" but following statements execute in the prior environment, i.e., the one that was current prior to the inline assignment.

If "cmd" is an external command, the shell has to fork in order to execute it. It can do that first, then perform the assignment, and finally execute the command. This ensures that the following statements execute in the prior environment.

If "cmd" is a shell function, forking is not an option because shell functions must be able to modify the environment. What exactly should be the effect of inline assignments for shell functions? My understanding is that if "cmd" is a shell function, then "cmd" should execute as if it had its own parameter "var" initialized with "foo". In other words, following statements should execute in an environment where the iniline assignment to "var" didn't happen, nor any assignments to "var" performed during the execution of "cmd".

One possible implementation is to execute the function by first declaring in its scope a local parameter "var", performing the assignment, and finally executing the function. An alternative is to save the state of the "var" parameter, perform the assignment, execute the function, and finally restore "var" to its saved state.

With both implementations, the function "cmd" executes as if it had its own parameter "var" and following statements execute as if none of the assignments to "var" had happened. However, the two implementations execute the function "cmd" in slightly different environments. With the former, the execution starts with an environment where "var" is a local parameter. With the latter, "var" is a parameter of a surrounding scope. This can lead to differing behaviors. For instance, "typeset var" prints "var=foo" with the former while with the latter it declares a local parameter "var" that will from then on hide the surrounding parameter "var" that equals "foo". While this can lead to dramatically different executions, chances of it happening in existing scripts looks very low. Thus, switching between the two implementations would not necessarily be too much of a problem.

With the advent of named references, the difference between the two implementations becomes more noticeable. Consider the following script:

typeset var=v0
typeset -n ref=var
function cmd {
  echo "A: var=$var - ref=$ref"
  var=v2;
  echo "B: var=$var - ref=$ref"
  ref=v3;
  echo "C: var=$var - ref=$ref"
}
var=v1 cmd
echo "D: var=$var - ref=$ref"


and the output produced by the two implementations:

Local parameter

Save and restore parameter

A: var=v1 - ref=v0

B: var=v2 - ref=v0

C: var=v2 - ref=v3

D: var=v3 - ref=v3

A: var=v1 - ref=v1

B: var=v2 - ref=v2

C: var=v3 - ref=v3

D: var=v0 - ref=v0


My intuitive understanding of inline assignments is that it gives the shell function its own instances of the assigned parameters. Thus, changes that it performs to these parameters are only visible within the function. However, in the example above, "ref" refers to the original "var". Therefore "$ref" should expand to the value of the original "var" and when the function assigns a value to "ref", it should affect the original "var". The change should a) not affect the "var" visible within the function and b) persist after the end of the execution of the function. That's exactly what happens with the former implementation but not at all with the latter one.

Ksh relies on the former implementation, or at least behaves like it. Zsh relies on the latter one.

The current behavior of Zsh looks wrong to me. I'm tempted to switch to the former implementation. As discussed above, there could be a few scripts that notice the change but I think that they should be sufficiently rare that we could do the change.

What do you think? Do you agree that the former implementation leads to a preferable/more intuitive behavior? Do you agree that we could change the implementation at the risk of changing the behavior of some existing scripts?

Philippe



Messages sorted by: Reverse Date, Date, Thread, Author