[Language Design] Should inline assignments create local parameters?

Philippe Altherr <[email protected]>
Newsgroups gmane.comp.shells.zsh.devel
Message-ID <CAGdYchvfg0D+eG6w3pdzZafHjeFxuJSKtE8VK+Kbg9LXQTX9Xw@mail.gmail.com>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.