20-CS-4003-001 Organization of Programming Languages Fall 2017
Parameter Passing

Lambda calculus, Type theory, Formal semantics, Program analysis

    Prev     Next     All lectures

Strict Evaluation

Scheme:

  (define tester
    (lambda (x y)
      (if (= x 1) x y)))

  (tester 1 (/ 3 0))

  (define fixer
    (lambda (x y)
      (if (= x 1) x (y))))

  (fixer 1 (lambda () (/ 3 0)))

Haskell:

  tester x y =
    if (x == 1) then [x] else y

Java:

  class A {
    int a[];
    public A () { a = new int[10000000]; }
  }

  public class call_by_value {
     public static void assign (A a, int i, int v) {
        a.a[i] = v;
     }

     public static int get (A a, int i) {
        return a.a[i];
     }

     public static void main (String args[]) {
        A a = new A();
        assign(a,1000000,34);
        System.out.println(get(a,1000000));
     }
  }

C:

  /* Code 1 */
  #include <iostream>
  using namespace std;

  class A {
   public:
     int a[1000000];

     A() {
        for (int i=0 ; i < 1000000 ; i++)
           a[i] = 0;
     }

     A (A &obj) {
        for (int i=0 ; i < 1000000 ; i++)
           a[i] = obj.a[i];
           cout << "in copy constructor\n";
     }
  };

  void assign (A a, int i, int v) {
     cout << "in assign\n";
     a.a[i] = v;
  }

  int main () {
     A a;
     assign(a,500000,34);
  }

  /* Code 2 */
  #include <iostream>
  using namespace std;

  class A {
   public:
     int a[1000000];

     A() {
        for (int i=0 ; i < 1000000 ; i++)
           a[i] = 0;
     }
  };

  void assign (A *a, int i, int v) {
     a->a[i] = v;
  }

  int get (A *a, int i) {
     return a->a[i];
  }

  int main () {
     A *a = new A();
     assign(a,500000,34);
     cout << get(a,500000) <lt; "\n";
  }

  /* Code 3 */
  #include <iostream>
  using namespace std;

  class A {
   public:
     int a[1000000];

     A() {
        for (int i=0 ; i < 1000000 ; i++)
           a[i] = 0;
     }
  };

  void assign (A &a, int i, int v) {
     a.a[i] = v;
  }

  int get (A &a, int i) {
     return a.a[i];
  }

  int main () {
     A a;
     assign(a,500000,34);
     cout << get(a,500000) << "\n";
  }

 -  In strict languages procedure arguments are evaluated completely before the body of the procedure is executed. There are several ways this can be achieved.

Applicative order: the arguments of a function are evaluated from left to right in a post-order traversal of reducible expressions. Applicative order evaluation reduces terms as much as possible before the function is applied. Scheme is an applicative order language. Program tester will evaluate argument y even if x evaluates to 1, making y irrelevant. For example, (tester 1 (/ 3 0)) results in a divide by 0 exception. However, wrapping a term in a thunk will delay its evaluation, even allow it to be controlled. Program fixer does this for tester: now

  (fixer 1 (lambda () (/ 3 0)))

does not cause an exception but

  (fixer 2 (lambda () (/ 3 0)))

does. Haskell is not an applicative order language. The Haskell program tester is similar to the Scheme program of the same name. However this input

  tester 1 [1..]

produces a value of [1] even though the second argument is an infinite list. An input of

  tester 2 [1..]

tries to generate that list.

Call by value: argument expressions are evaluated, and those values are bound to their corresponding variables in the function. This is typically done by copying the value into a section of memory that is accessed only by the procedure. Any value that is passed into a procedure is unchanged in the caller's scope when the procedure returns. The order of evaluation may be right to left, left to right, or unspecified. If a large structure is passed, the entire structure, technically, should be copied. However, in Java, only references to an object are passed. But then changes to data within the object are possible after the procedure returns. Java program call_by_value shown on the left passes a reference to an A object into get and assign. Observe that the array of the A object is changed. This is technically called call-by-value because it is the value of the reference that is passed. In other words, the a variable is declared as a reference, not an actual object, and it can be made to reference any other A object. In C an object can be passed by value into a procedure using a copy constructor as shown in Code 1 to the left. But an object's reference can also be passed in Java style as shown in Code 2.

Call by reference: argument expressions evaluate to a variable address which is passed to the procedure. The procedure may then change the value of the variable as it may operate directly on the variable. One might consider Code 2 an example of call-by-reference although technically it is call-by-value on the value of the reference variable, namely a.

Call by value-result: call by value except that the value of the variable that is named as argument gets set to the final value its copy had in the procedure. C language program Code 3 shows what this might look like but notice a copy of the object is not made so this is technically not call-by-value-result.