CSE130 LECTURE NOTES

June 3, 2002
 
 

SYNTAX OF PARAMETER-PASSING

Remember the rule of alpha-conversion: the meaning of a block is unchanged if a name that is bound locally in the block is changed everywhere inside the block.

In most languages alpha-conversion is a valid rule for formal parameters.  This implies that the details which are hidden from the user of a function or procedure include the names of formal parameters.

So which actual arguments match which formals should not be specified by name. It is usually specified by position: the parameter type of a function is a tuple type.

In Ada arguments can be specified by name using the same notation as for record aggregates. For example a function call might be:

integrate(epsilon => 0.001, f => sine, a => 0.0, b => pi);
This allows actual arguments to be given out of order, and default values can be used for unspecified arguments: e.g. epsilon above could have been left out if the default value was satisfactory. Common Lisp also has named parameters.
 
 

SEMANTICS OF CALLING FUNCTIONS AND PROCEDURES

Informally, the semantics of calling a function or procedure is as follows: We need to clarify each of these four stages.

The point to notice here is that expressions are never passed as arguments in mainstream languages. They are always evaluated first, and the result values are passed.

Remember the discussion of evaluation order for expressions. An operation like + applied to two arguments, e.g. 2*x + 3*y is really a function-call  plus(times(2,x),times(3,y)).

The most important rule of expression evaluation is that arguments are evaluated before the function applied to them is called, but if-then-else is special.  Languages differ in whether or not it is specified in what order multiple arguments are evaluated.
 
 

CALL BY VALUE

In the following procedure declaration f1 and f2 are formal parameter names and x is the name of a local variable.
procedure p(f1,f2:t);
    var x: t;
begin
    ...
end;
Given the call "p(e1,e2)" where e1 evaluates to the value a1 and e2 evaluates to the value a2, the simplest semantics of parameter-passing says that the call is equivalent to executing the block
begin
    const f1 := a1;
    const f2 := a2;
    var x: t;
    ...
end;
This method of parameter-passing is called call-by-value. A variant allows f1 and f2 to be local variables as opposed to local constants.
 
 

CALL BY VALUE-RESULT

When an actual argument is an expression, it makes no sense to use it for returning a result value. However when an actual argument is a variable name, the final value of the formal parameter can be copied back into the actual argument variable. More formally, given
procedure p(inout f:t);
begin
    ...
end;
the call "p(x)" where x is the name of a variable is equivalent to
 
begin
    var f:t := a; // "a" denotes the current value of x
    ...
    x := f;
end;
Call by value-result is used in Ada.
 
 

CALL BY REFERENCE

When an actual argument is a variable name, it makes sense to pass the location denoted by the name to the called procedure. Then the formal parameter name acts as a local name for the global location.

This is the semantics of Pascal "var" parameter-passing. We can explain it more formally as follows. Given

procedure p(var f:t);
begin
    ...
end;
the call "p(x)" where x is the name of a variable is equivalent to
begin
    const f: pointer to t := location(x);
    ...
end;
In some languages, for example C, call-by-reference is not available but in can be achieved by using call-by-value and passing the location of a variable instead of its value.
 
 

CALL BY MACRO

The idea in call-by-macro is that the actual argument is textually inserted into the called block.  This contradicts the statement earlier that the first step in the semantics of function-calling is to evaluate all the argument expressions!

Consider the pseudo-Pascal summation function

function sum (lo,hi:int; macro k:int; macro f:real): real;
    var s: real := 0;
begin
    for k := lo to hi do s := s + f;
    return s;
end;
With call-by-macro the actual argument expressions for k and f will be evaluated again each time the formal parameter is used, so we can write for example sum(1,n,j,a[j]*b[j]) to compute the dot-product of two vectors a and b of length n.
 
 

HIGHER_ORDER FUNCTIONS REPLACE CALL-BY-MACRO

All the other parameter-passing methods are basically similar: they work by initializing the formal parameters as local names in an environment used for executing the called block. They are all variants of "call-by-environment."

Consider the pseudo-Pascal summation function using call-by-macro from above.  This can be implemented using call-by-value with function arguments:

function sum (lo,hi:int; ref k:int; f:void->real);
    var s: real := 0;
begin
    for k := lo to hi do s := s + f();
    return s
end;

function f(): real;
begin
    return a[j]*b[j];
end;

The lesson to learn here is that higher-order functions give the functionality of call-by-macro.
 
 


Copyright (c) 2002 by Charles Elkan.