Skip to contentSkip to conversation

static vs. dynamic scoping

DISCUSSION Eric Blake 3 participants 4 messages Nov 9, 2010 – Nov 14, 2010

lore
Share & tools
lore

Eric Blake

author
#0 lore raw

On the Austin Group mailing list, David Korn (of ksh93 fame) complained[1] that bash's 'local' uses dynamic scoping, but that ksh's 'typeset' uses static scoping, and argued that static scoping is saner since it matches the behavior of declarative languages like C and Java (dynamic scoping mainly matters in functional languages like lisp):

[1]
https://www.opengroup.org/sophocles/show_mail.tpl?CALLER=show_archive.tpl&source=L&listname=austin-group-l&id=14951

I'm trying to standardize the notion of local variables for the next revision of POSIX, but before I can do so, I need some feedback on two general aspects:

1. Implementation aspect:
How hard would it be to add static scoping to dash?
Is it something that should be added in addition to dynamic scoping,
via the use of an option to select the non-default mode (for example, 'local -d' to force dynamic, 'local -s' to force static, and 'local' to go with default scoping)?
Or should dash switch entirely to static scoping (my gut feel is that
static scoping may be more efficient to implement, which fits in line with dash's desire to be as lean as possible)?

2. User aspect:
Is anyone aware of a script that intentionally uses the full power of
dynamic scoping available through 'local' which would break if scoping switched to static?

Here's a sample shell script that illustrates the difference between the two scoping methods (note that ksh only provides nested scoping via its typeset builtin, and only when using the function reserved word).

$ ksh -c 'function f1 { typeset a=local; f2; echo $a; };
  function f2 { echo $a; a=changed; };
  a=global; f1; echo $a'
global
local
changed
$ dash -c 'f1 () { typeset a=local; f2; echo $a; };
  f2 () { echo $a; a=changed; };
  a=global; f1; echo $a'
local
changed
global

In static scoping, function f2 does not shadow a declaration of a, so references to $a within f2 refer to the global variable. The local variable a of f1 can only be accessed within f1; the behavior of f2 is the same no matter how it was reached.

In dynamic scoping, function f2 looks up its call stack for the closest enclosing scope of a variable named a, and finds the local one declared in f1. Therefore, the behavior of f2 depends on how f2 is called.

Signature · 2 lines
Eric Blake   [email protected]    +1-801-349-2682
Libvirt virtualization library http://libvirt.org
#1 lore raw
Quoted 4 lines · Eric Blake

2. User aspect:
Is anyone aware of a script that intentionally uses the full power of
dynamic scoping available through 'local' which would break if scoping switched to static?

FWIW, some scripts use "local IFS" to reset IFS to a sane value, and have the shell take care of restoring it afterwards. This works with ksh's typeset, but because of the static scoping only so long as no other shell functions get called, or those other shell functions also take the effort to handle IFS correctly. In the scripts I have been able to find, other shell functions do get called, but they themselves set IFS too. Still, it may be worth keeping in mind, if only to serve as one of the few examples of when dynamic scoping would have been slightly more useful.

#2 lore raw

How is dynamic scoping in this case 'more useful'? Imagine function a written by author A and function b written by author B, both loaded into an application and each of them working independently, bound together by an api spec.
Now function a uses typeset IFS="X" to set the field separator to "X" via a local variable IFS. If function b now uses "read foo1 foo2 foo3" to read a line of a database the concept of dynamic scoping *BITES*. Suddenly function b no longer works as author B intended it. Lots of surprise, lots of raised eyebrows by an unwanted interaction. Static scoping is in this case much *SAFER* because IFS is reset in function b to the (global) default.

The issue isn't new but the issues with dynamic scoping are one major factor why there are so few shell function libraries: It is just hard to write such a library without running afoul dynamic scoping.

Olga

Quoted 20 lines · Harald van Dijk
Quoted 5 lines · Eric Blake

2. User aspect:
Is anyone aware of a script that intentionally uses the full power of
dynamic scoping available through 'local' which would break if scoping switched to static?

FWIW, some scripts use "local IFS" to reset IFS to a sane value, and have the shell take care of restoring it afterwards. This works with ksh's typeset, but because of the static scoping only so long as no other shell functions get called, or those other shell functions also take the effort to handle IFS correctly. In the scripts I have been able to find, other shell functions do get called, but they themselves set IFS too. Still, it may be worth keeping in mind, if only to serve as one of the few examples of when dynamic scoping would have been slightly more useful.
--
To unsubscribe from this list: send the line "unsubscribe dash" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Signature · 6 lines
      ,   _                                    _   ,
     { \/`o;====-    Olga Kryzhanovska   -====;o`\/ }
.----'-/`-/     [email protected]   \-`\-'----.
 `'-..-| /       http://twitter.com/fleyta     \ |-..-'`
      /\/\     Solaris/BSD//C/C++ programmer   /\/\
      `--`                                      `--`
#3 lore raw
Quoted 3 lines · ольга крыжановская

Now function a uses typeset IFS="X" to set the field separator to "X" via a local variable IFS. If function b now uses "read foo1 foo2 foo3" to read a line of a database the concept of dynamic scoping *BITES*.

The way I had seen "local IFS" used is to sanitise IFS -- to reset IFS to the default value, when IFS was set to, for example, : before the function was called, and the function wants to split on whitespace. In that case, you don't want b to get IFS=:. Both forms are used, and in your form, I agree, static scoping is more useful.