On this page:
1.1 Extra Arguments
1.2 Global Variables

1 Alternatives to Dynamic Scope

A dynamically-scoped binding is active for the whole dynamic extent of the evaluation of the body of the binding form. For instance:

{with {n 5}
      {f 10}}
if with introduces identifiers with dynamic scope, then n→5 is available anywhere during the evaluation of {f 10}.

Suppose f calls g, which calls h, which calls i, and i uses n.

Without dynamic binding, there are two options for our code above to communicate n→5 to the body of i. Unfortunately, both are either impractical, error prone, or incorrect.

1.1 Extra Arguments

The first option is to pass n as an extra argument to f:

{with {n 5}
      {f 10 n}}

Of course, this is not sufficient: all the functions involved in the evaluation, ie. g, h, and i, must also accept an extra argument for n. While this makes sense for i (after all, it is the one using the value of n), the problem is the intermediate functions g and h, which have to accept and pass around parameters they are not concerned with.

This approach does not scale: there may be many intermediate functions potentially involved in the flow between a binding definition site and a binding usage site. Also, there may be many such bindings. Quickly, all functions in the system will end up accepting a large amount of useless parameters just to be able to support the possibility that someone might need to customize them.

1.2 Global Variables

The other approach, which has the benefit of not involving intermediate functions, is to make n a global variable.

{define n 0}

Globally-accessible identifiers can be an issue because they are visible everywhere, which might not be desired. So a better solution might be to (try to) make n locally accessible only to our code and to i. Note that this might not be possible if these functions are unrelated in the lexical/module structure of the program. So in practice, global variables end up being used for that purpose.

But the worst problem is that we want to allow our code to temporarily customize the value of n. So n should be a mutable variable which should raise a number of alarms!

{set! n 5}
{f 10}

Suppose we have two threads of execution, T1 and T2. T1 runs our code above, and T2 runs another code that sets n→10. What value of n will be seen by function i? Without proper synchronization, it can be either 5 or 10 or some garbage. With proper synchronization, it can be 5 or 10, depending on the actual interleaving between both threads. This is not the intended semantics!

Question: Write a small program and a possible execution trace that illustrates why the obtained semantics with global variables is not intended.