CSE 341 -- Parameter Passing

The following techniques are used to pass arguments in traditional imperative languages: Call by value and call by name are also important concepts for functional languages. Other techniques:

call by value: copy going into the procedure

call by result: copy going out of the procedure

call by value result: copy going in, and again going out

call by reference: pass a pointer to the actual parameter, and indirect through the pointer. If the actual parameter is a variable or an array element (not an expression), then the procedure can assign to the formal parameter and as a result assign into the actual parameter as well. (The more general programming language term for "variable or array element" or the like is l-value, in other words, something that can be on the left hand side of an assignment statement.)

call by name: re-evaluate the actual parameter on every use. For actual parameters that are simple variables, this is the same as call by reference. For actual parameters that are expressions, the expression is re-evaluated on each access. It should be a runtime error to assign into a formal parameter passed by name, if the actual parameter is an expression. One possible implementation: use anonymous function ("thunk") for call by name expressions. (This is traditionally used to implement call by name in languages such as Algol and Simula.)

Call by name is not supported in recent languages, but is still an important concept in programming language theory. (In newer languages, if you want the effect of call by name, pass a procedure as a parameter.)

For a pure functional language, lazy evaluation has exactly the same semantics as call by name, but will generally be more efficient. With lazy evaluation, the actual parameter is not evaluated until the formal parameter's value is needed. At that time, the actual parameter is evaluated, and the value is cached. Then, if the value of the formal parameter is needed again, the cached value will be used. (This is safe in a pure functional language, since the value of an expression will always be the same each time it is evaluated.) Thus, with lazy evaluation, an actual parameter is evaluated either 0 or 1 times.

Call by value is particularly efficient for small pieces of data (such integers), since they are trivial to copy, and since access to the formal parameter can be done efficiently. If we use call by value with pointer semantics, it is also efficient for large objects. Java uses call by value for primitive data types, and call by value with pointer semantics for objects.

Call by reference is also efficient for large pieces of data (such as large arrays), since they don't need to be copied.

Fortran uses call by reference
Insecurity in early FORTRANs -- passing a constant allowed the procedure to change the constant!

Algol 60 has call by name, call by value

For objects (not primitive types), Java uses call-by-value with pointer semantics. In other words, Java passes a copy of the reference to the object to the called method (but doesn't copy the object itself). Smalltalk and Scheme work in exactly the same way. Unless you assign into the formal parameter, this gives the same answers as call-by-reference. However, if you assign into the formal parameter, the link with the actual parameter is broken. (Actually, Smalltalk won't let you assign into the formal parameter, but Java and Scheme will.)

An important related concept: aliasing. Two variables are aliased if they refer to the same storage location. A common way that aliasing can arise is using call by reference. A formal parameter can be aliased to a nonlocal variable, or two formal parameters can be aliased.


Things to Know for the Final

You should understand call by value, call by reference, call by name, and aliasing. You should understand examples where call by value and call by reference give different results, and examples where call by value and call by name give different results. There won't be any questions about call by result or call by value result.


Examples of Passing Parameters in Java

Here are two examples, adapted from The Java Programming Language by Ken Arnold and James Gosling.

Passing a primitive type as a parameter:
class PassByValue {
  public static void main(String[] args) {
    double x = 1.0;
    System.out.println("before: x = " + x);
    halveIt(x);
    System.out.println("after: x = " + x);
  }

  public static void halveIt(double arg) {
    arg = arg/2.0;
    System.out.println("halved: arg = " + arg);
  }
}
Sample output:
before: x = 1.0
halved: arg = 0.5
after: x = 1.0
Here is another example, in which we pass an object as a parameter.
import java.awt.Point;

class PassObject {
  public static void main(String[] args) {
    Point p = new Point(10,20);
    System.out.println("before halveX: p.x = " + p.x);
    halveX(p);
    System.out.println("after halveX: p.x = " + p.x);

    setIt(p);
    System.out.println("after setIt: p.x = " + p.x);
  }

  public static void halveX(Point q) {
    q.x = q.x/2;
    System.out.println("halved: q.x = " + q.x);
  }

  public static void setIt(Point q) {
    q = new Point(0,0);
    System.out.println("setIt: q.x = " + q.x);
  }
}
Sample output:
before halveX: p.x = 10
halved: q.x = 5
after halveX: p.x = 5
setIt: q.x = 0
after setIt: p.x = 5

Examples of Passing Parameters in Ruby

Parameters in Ruby are also passed by value with pointer semantics. Here are some examples.

def test1 a
  a = 10
  print "in test1 -- a = "
  print a
end

def test2 a
  a[0] = 100
  print "in test2 -- a = "
  print a
end

def test3 a
  a = [200]
  print "in test3 -- a = "
  print a
end

# try these examples

# first a simple example of passing an integer
# j = 5
# test1 j
# afterwards j is still 5

# x = [1,2,3]
# When we call test2, we pass the argument by value, but the value is 
# a reference to the array x.  test2 assigns something to the 0th element 
# of its argument, and so x is changed.
# test2 x
# x


# test3 on the other hand assigns a new array to its formal parameter, so
# is unchanged
# test3 x
# x

Call by Reference Examples

Suppose that Ruby used call by reference instead of call by value. We would get a different result for the test1 and test3 examples above.

j = 5
test1 j
# afterwards j would now be 10 if it were passed by reference!


x = [1,2,3]
test3 x
# afterwards x would be the array [200]
Here is a Racket example. If we try to define swap as a function, it doesn't work, again because Racket uses call by value:
(define (bad-swap a b)
  (let ([tmp a])
    (set! a b)
    (set! b tmp)))

(let ([x 3]
      [y 4])
  (printf "calling bad-swap\n")
  (bad-swap x y)
(list x y))
The result is (3 4) -- x and y are unchanged.

However, defining swap as a macro does work.

(define-syntax-rule (swap a b)
  (let ([tmp a])
    (set! a b)
    (set! b tmp)))

(let ([x 3]
      [y 4])
  (printf "calling  (good) swap\n")
  (swap x y)
  (list x y))
The result this time is (4 3) -- we did swap x and y.