Functions and Side Effects
Side Effects
-
Side effect:
a change in the program's state caused by an expression or a
function call, or modification of a global variable in a procedure or
function
- Program state: values of variables or status of I/O
- Java Example: i = j++;
- Java Example: Collection add(Object o) returns true if the collection changed
- Side effects make it harder to reason about program behavior:
- harder to debug
- harder to prove that the program is correct
Side Effects and Ada Function Parameters
- Ada does not allow a function to introduce side effects through its parameters
(except via pointers).
-
For example, consider the following ILLEGAL function definitino:
-- Will not compile
-- Is supposed to swap the parameters and return their sum
function sumAndPutInOrder(i, j: in out Integer) return Integer is
begin
if i < j then
swap(i, j);
end if;
return i + j;
end sumAndPutInOrder;
i: Integer := 3;
j: Integer := 2
begin
put("Average is: ");
put( sumAndPutInOrder(i, j) / 2 ); // Change i here!
new_line;
put("Smallest is");
put(i);
new_line;
end;
Side Effects and Global Variables
- Side effects also occur when a procedure or function changes a
global variable
-
Global variable: A variable is global with respect to a section of code if it is visible in the
section, but not declared in that section.
- When no globals are used, all communication with a procedure must be
done using parameters (or return values for functions)
- A side effect involving a global variable is hard to find
because it's not possible to examine parameter lists to find which
procedure/function could have modified a variable.
- When this can occur, it is necessary to search the
body of all called routines when looking for an error.
-
When there
are no side effects, then the body of a routine only needs to be
searched if it has an out parameter of interest.
- Example:
g: Integer := 1; -- Global variable
procedure doSomething(p: out Integer) is
begin
p := 0;
end doSomething;
procedure doSomethingElse(p: in Integer; q: out integer) is
begin
q := 2 * p;
end doSomething;
procedure putHeader is
begin
put("Something");
g := 0; -- Whoops!
end putHeader;
...
x, y: Integer;
begin
put(g); -- g is correct here
putHeader;
doSomething(x);
doSomethingElse(x, y);
put(g); -- g is NOT correct here
-- What happened? putHeader should NOT have changed it!