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. Java uses call by value for primitive data types.
Call by reference is particularly 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
Ada uses different designations: IN
, OUT
,
IN OUT
:
IN
is the same as
call by value, OUT
is the same as call by result, and IN
OUT
is the same as call by value result. In addition,
IN
parameters are local constants -- you can't assign into them.
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.
begin integer n; procedure p(k: integer); begin n := n+1; k := k+4; print(n); end; n := 0; p(n); print(n); end;Note that when using call by reference,
n
and k
are aliased.
Output:
call by value: 1 1 call by value-result: 1 4 call by reference: 5 5
begin integer n; procedure p(k: integer); begin print(k); n := n+10; print(k); n := n+5; print(k); end; n := 0; p(n+1); end;Output:
call by value: 1 1 1 call by name: 1 11 16
This example illustrates assigning into a parameter that is passed by reference or by name
begin array a[1..10] of integer; integer n; procedure p(b: integer); begin print(b); n := n+1; print(b); b := b+5; end; a[1] := 10; a[2] := 20; a[3] := 30; a[4] := 40; n := 1; p(a[n+2]); new_line; print(a); end;
Output:
call by reference: 30 30 10 20 35 40 call by name: 30 40 10 20 30 45
fun f x = 42; val ans = f (1 div 0);In ML, this gives an error, since f evaluates its argument. If call-by-name were used, ans would just be bound to 42, since the argument isn't used.
We can simulate call-by-name in ML by wrapping the argument in a function:
fun g x = 42; fun h x = x()+1; val ans1 = g (fn() => 1 div 0); val ans2 = h (fn() => 100);
Now consider passing a reference. We still use call-by-value, but in this case the value is a reference (that can be assigned into).
fun zeroit r = (r := 0); val squid = ref 100; zeroit squid; val result = !squid;
The following code is also available as params.m
An 'if' function in Miranda. Since it uses lazy evaluation, the true or false branches only get evaluated if its value is needed.
my_if True x y = x my_if False x y = yUsing the if function:
my_if (x>y) 3 4 my_if (x>y) 3 (1/0)The infinite list of ones:
ones = 1 : onesThe infinite list of integers:
ints n = n : ints (n+1) [1..]The infinite string "abcabcabc ..."
lets = "abc" ++ letsTwo prime number programs:
factors n = [k | k <- [1..n]; n mod k = 0] dullprimes = filter isprime [2..] where isprime p = (factors p = [1,p]) interestingprimes = sieve [2..] where sieve (p:x) = p : sieve [n | n <- x; n mod p > 0]Hamming numbers. The Hamming numbers are all numbers of the form 2**i * 3**j * 5**k for all i,j,k >= 0. The ham function should return them in order, starting with 1.
my_merge (x:a) (y:b) = x : my_merge a (y:b) , if x<y = y : my_merge a b , if x=y = y : my_merge (x:a) b , otherwise ham = 1: my_merge ham2 (my_merge ham3 ham5) ham2 = map (*2) ham ham3 = map (*3) ham ham5 = map (*5) ham
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.0Here 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