More precisely, a function is an abstraction of an expression. All the user of the function needs to know is what value will be returned. Exactly how this value is computed is a detail that is private to the function.
Consider for example two alternative cosine functions:
Each function "encapsulates" a very different expression, but for the same argument, both always yield the same result.function cosine (x: real): real;
begin
return sine(x + pi/2.0);
end;function cosine (x: real): real;
begin
return integrate(sine,0,x,epsilon);
end;
Just like for functions, procedures with different bodies and different
algorithms can have exactly the same effect, i.e. they are the same
from the user's abstract point of view.
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:
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.integrate(epsilon => 0.001, f => sine, a => 0.0, b => pi);
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.
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 blockprocedure p(f1,f2:t);
var x: t;
begin
...
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.begin
const f1 := a1;
const f2 := a2;
var x: t;
...
end;
the call "p(x)" where x is the name of a variable is equivalent toprocedure p(inout f:t);
begin
...
end;
Call by value-result is used in Ada.begin
var f:t := a; // "a" denotes the current value of x
...
x := f;
end;
This is the semantics of Pascal "var" parameter-passing. We can explain it more formally as follows. Given
the call "p(x)" where x is the name of a variable is equivalent toprocedure p(var f:t);
begin
...
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.begin
const f: pointer to t := location(x);
...
end;
Copyright (c) 1999 by Charles Elkan.