8. Subprograms

8.1. What is a subprogram?

A subprogram is a chunk of code that is independent from other parts to some extent. In most languages, subprograms have the following properties.

  • There is a single entry point. (But Fortran provides multiple entry points.)

  • When a subprogram A calls another subprogram B, A will pause until B terminates. Namely, there is only one subprogram that is executed at a time. (This is not the case for concurrent languages.)

In some languages, a subprogram that returns a value is called a function and one that does not is called a procedure or a subroutine, but both types of subprograms are called functions in other languages such as C. In addition, object-oriented languages usually use the term methods for them.

8.2. Parameter

A parameter is used in order to apply a single subprogram to various data.

Actual parameters ::

Data specified when a subprogram is called.

Formal parameters ::

Variable names used in a subprogram to store actual parameters.

  • In many languages, acutal parameters are associated to formal parameters by their positions (positional parameters). It is error-prone when a subprogram has many parameters. In some languages, it is possible to specify a formal parameter for each actual parameter when calling (keyword parameters).

  • In many lanuages, the number of actual parameters and formal parameters must be the same. In some languages, it is possible to declare default values for formal parameters so that we can omit some actual parameters.

  • In script lanuages such as Ruby, more than one actual parameters can be combined into an array to be stored in a single formal parameter, and vice versa.

Parameters in Ada

Suppose we have the following declaration in Ada.

procedure Draw_Rect( X , Y : Integer;
                     Width : Integer := 10;
                     Height : Integer := 10;
                     Color : Integer := 0 )

We may write any of the following procedure calls.

Draw_Rect( 100, 50, 20, 10, 1 )
Draw_Rect( Width => 20, X => 100, Y => 50, Color => 1 )
Draw_Rect( 100, 50, Width => 20, Color => 1 )

Parameters in Ruby

When a formal parameter has an asterisk, any number of actual parameters are combined into an array.

def foo(*a)
  puts a  # output [1, 2, 3]
end

foo(1, 2, 3)

When an actual parameter is an array and formal parameters are enclosed by a parenthes, elements of the array will be stored in the formal parameters one by one. Surplus elements are ignored.

def bar((a,b))
  puts a  # output 1
  puts b  # output 2
end

bar([1,2,3])

8.3. Direction of parameter passing

Input

Data are passed from actual parameters to formal parameters when a subprogram is called.

Output

Data are passed from formal parameters to actual parameters when a subprograum finishes its execution.

In case of output, the actual parameter must be assignable, i.e. an expression that has a left value (Section 4.3), which is typically a variable.

Passing parameters in Ada

procedure Foo(A : in Integer;
              B : in out Integer;
              C : out Integer) is
begin
  B := B + 1;
  C := A + B;
end Foo;

8.4. Evaluation strategy of parameters

8.4.1. Pass by value (call by value)

First, all the actual parameters are evaluated. Then, the values are copied to formal parameters.

  • It is usually used for input direction. In case it is used for both input and output, we specifically call it pass-by-copy-restore.

8.4.2. Pass by reference (call by reference)

Formal parameters are bound to entities referred by associated actual parameters. In other words, formal parameters become aliases (Section 3.3.2) of actual parameters.

  • Its direction is inherently both input and output.

Passing parameters in Pascal

In Pascal, a foraml parameter declared with var is pass-by-reference, otherwise pass-by-value (input only).

For example, the following procedure does nothing at all.

procedure swap(a, b : integer)
  temp : integer;
  begin
    temp := a;
    a := b;
    b := temp
  end;

If we change the first line as follows, it will swap values of two parameters.

procedure swap(var a, b : integer)

8.4.3. Pass by name (call by name)

Actual parameters are not evaluated when a subprogram is called. They are evaluated each time associated formal parameters are used in the body of subprograms. The environment used for evaluation is the one at the place where the subprogram is called.

  • In Algol, an assignment to a formal parameter implies an assignment to the associated actual parameter (provided it is assignable), which means the direction is input and output. In Scala, an assignment to a formal parameter is prohibited, so the direction is input only.

  • Precisely speaking, there are two methods to implement pass-by-name.

    • Actual parameters are evaluated each time formal parameters are used.

    • Actual parameters are evaluated at the first time formal parameters are used, and the values are memoized. After that, the memoized values are used, and actual parameters are not evaluated. This method is also called pass-by-need to distinguish it from the previous method.

    Results of the two methods are the same as long as there is no side-effect, but they may be different if actual parameters or subprograms have side-effect.

Passing parameters in Scala

var x = 0
def f(a: => Int) = {
    print(a)  // output 1
    x = 2
    print(a)  // output 3
}
f(x+1)

Exercise 16

Suppose we have the following C-like program.

void main() {
  int x = 2, list[5] = {1, 3, 5, 7, 9};
  swap(x, list[0]);
  swap(list[0], list[1]);
  swap(x, list[x]);
}

void swap(int a, int b) {
  int temp;
  temp = a;
  a = b;
  b = temp;
}

Answer the values of variables x and list after execution when we use the following evaluation strategy of parameters, respectively.

  1. pass-by-value (input only)

  2. pass-by-reference

  3. pass-by-name (input and output, evaluate each time)

NOTE: When using pass-by-name, assume that an assignment to a formal parameter implies an assignment to the associated actual parameter, thus the direction is input and output.

[Answer]

8.5. Closure

In languages such as Lisp, Ruby, JavaScript, etc., a function is a first-class object (i.e. it can be assigned into a variable, passed as a parameter, etc.).

Suppose we put a function into a variable and call it later.

In case the language has static scope (Section 3.4), variables in the function should be evaluated in the environment at the place the function is defined (not the place the function is called).

In order to do that, we need to prolong extent (Section 4.5) of variables of outer blocks so that the stored function can use those variables. This feature is called a closure.

closure in JavaScript

function get_increment_function() {
    var x = 1;
    return function() { return x += 1; };
}

var inc1 = get_increment_function();
console.log(inc1());  // 2
console.log(inc1());  // 3

var inc2 = get_increment_function();
console.log(inc2());  // 2

console.log(inc1());  // 4

Exercise 17

Suppose we want to create 10 buttons in a web page, each of which will show its id number when clicked. But the following JavaScript code does not work properly. Explain why.

var b = document.getElementsByTagName("input");
        // Returns an array of 10 button objects.
let i;
for (i = 1; i <= 10; i++) {
    b[i-1].onclick = function(){ alert(i); };
}

[Answer]