cscopedynamic-bindingdynamic-scopecallbyname

Call by Name with dynamic scoping


I am stuck at the following problem on static/dynamic scoping:

The following program fragment is written in a programming language that allows global variables and does not allow nested declarations of functions.

 global int i = 100, j = 5; 
 void P(x) { 
  int i = 10; 
  print(x + 10); 
  i = 200; 
  j = 20; 
  print (x); 
 } 
 main() {P(i + j);} 

Q1. If the programming language uses static scoping and call by need parameter passing mechanism, the values printed by the above program are

(A) 115, 220 (B) 25, 220 (C) 25, 15 (D) 115, 105

Q2. If the programming language uses dynamic scoping and call by name parameter passing mechanism, the values printed by the above program are

(A) 115, 220 (B) 25, 220 (C) 25, 15 (D) 115, 105

What I think:

On Q1: As it's static scoping and as per call by need, x should be replaced with i + j. But it will cause a local name conflict as there is already a variable with name i. So it (the global i) might be renamed, lets say to i1, and then call will be:

   first call: print(x+10) -> (i1 + j + 10) -> (100 + 5 + 10) -> 115
   second call: print(x) -> print(i1 + j) -> 105 (Already evaluated - call by need)

On Q2: In dynamic scoping, you search for a variable in the local function first, then you search in the function that called the local function, then you search in the function that called that function, and so on, up the call stack.

As per call by name:

print (i1 + j + 10) -> print (100 + 5 +10 ) -> 115

And the second call will be

print(x) -> print(i1 + j) -> (100 + 20) = 120 // Evaluate again - Call be name.

Is this answer correct? (Not present in the options) Is there something I'm missing? (Dynamic binding may be?)


Solution

  • Q1

    OP's answer is correct (D). In fact, because global i is not modified during the execution of P, there is no difference between call by need and call by value.

    Here is an example where it does make a difference:

    global int i = 100, j = 5;
    
    void IncreaseTheGlobal() {
        i = i + 1;            // static scoping means this is the GLOBAL i!
        print(i);
    }
    
    void P(x) {
        int i = 10;
        IncreaseTheGlobal();  // 101 (increased global i)
        print(i);             //  10 (local i)
        print(x);             // 106 (x is evaluated; picks up increased global i)
        IncreaseTheGlobal();  // 102 (2nd time increased global i)
        print(x);             // 106 (x not re-evaluated; unaffected by 2nd increase)
    }
    
    main() {
        print(i);             // 100 (original global i)
        P(i + j);
        print(i);             // 102 (new global i)
    }
    

    As already pointed out by OP, the first time x is evaluated, it picks up whatever value global i has at that particular moment. After that initial evaluation, x is no longer affected by a later modification of global i.

    Q2

    Call by name is typically used in macro languages. So why not use the best known macro language ever: the C preprocessor?

    #include <stdio.h>
    
    int i = 100, j = 5;
    
    #define print(num)  printf("%d\n", num)
    
    #define P(x) {     \
        int i = 10;    \
        print(x + 10); \
        i = 200;       \
        j = 20;        \
        print(x);      \
    }
    
    main() {
        P(i + j);
    }
    

    Compile, run, and see: 25, 220.

    Call by name works with a simple search-and-replace; within the body of P, replace every occurrence of x by i + j.

    int i = 10; 
    print(i + j + 10);    // 10 + 5 + 10 = 25
    i = 200;
    j = 20;
    print(i + j);         // 200 + 20 = 220
    

    In other words, the i and the j inside i + j just pick up the present value of whatever happens to be in scope when x is evaluated.

    So the correct answer is B, right? Well, almost... the correct answer depends on the implementation of print. Suppose print practices call by name too, and print defines its own local variable i, then that would dramatically change the result. Try this:

    #define print(num)  { int i = 0; printf("%d\n", num); }
    

    The result now changes to 15, 20.

    This is probably the most important reason why dynamic scoping is bad for the maintainability of your code. Implementation changes in function print (even something trivial as changing the name of a local variable) might break functions on a higher level.