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
- Side effects can be unavoidable:
- Get(x); changes the state of the input
- Put(x); changes the state of the output
- Java Examples:
- Collection add(Object o) returns true if the collection changed
- i = j++;
- i = s.pop(); // pop and return top
Why We Avoid Side Effects
- Side effects make it harder to reason about program behavior:
- harder to debug
- harder to prove that the program is correct
- What is the output: i=3; i = i++; S.o.p(i);
- What is the output: i=3; i = ++i; S.o.p(i);
Side Effects and Ada Function Parameters [Changed in Ada 2012]
- Ada [prior to Ada 2012] does not allow a function to introduce side effects through its parameters
(except via pointers).
-
For example, consider the following ILLEGAL [pre 2012] function definitino:
-- Will not compile [pre 2012]
-- 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;
Why the change: one reason is truth in advertising: functions could cause side effects via
globals or parameters that are pointers
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)
Debugging, Side Effects, and Global Variables
- 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: Debugging, Side Effects, and Global Variables
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(g, y);
put(g); -- g is NOT correct here
-- What happened? putHeader should NOT have changed it!
end;
Example Revised: Debugging, Side Effects, and Global Variables
- Example2: Remove the global and the code no longer compiles
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! -- Won't compile
end putHeader;
...
g: Integer := 1; -- Not a Global variable
x, y: Integer;
begin
put(g); -- g is correct here
putHeader;
doSomethingElse(g, y);
doSomething(g);
put(g); -- g is NOT correct here
-- What happened? putHeader should NOT have changed it!
end;
OLD NOTES ON SIDE EFFECTS
Java and C Can Ignore Expression Results
public class Func {
public static int calcPrintCube(int n){ // Non-void return type
System.out.println(n * n * n); // Side effect: change state of output
return n * n * n;
}
public static void main(String[] args) {
calcPrintCube(10); // function used as statement
int cube = calcPrintCube(20); // return value ignored
}
}
This is legal C:
int calcPrintCube(int n){ // Non-void return type
printf("%d", n * n * n); // Side effect: change state of output
return n * n * n;
}
void main(String args[]) {
calcPrintCube(10); // function used as statement
int cube = calcPrintCube(20); // return value ignored
}
Even this is legal C:
x = 0; // Do you
y - 0; // See the error
z = 0; // Error in this code?
In C, results of expressions can be freely ignored
Side Effects
- calcPrintCube above has a side effect
- A function that modifies state causes a side effect
- Ada tries to avoid side effects
- A procedure or function that modifies a global is also said to have a side effect
- We will discuss side effects more later. Here are
some notes on side effects.
What is a Statement?
- A statement modifies the program state:
- Program state:
- values of all variables
- input marker
- output so far
- Open files
- ...
- Examples:
- x := 3;
- get(y);
- put(x);
-
Open (File => f, Mode => In_File, Name => "my_file.txt");
- Contrast: Statements modify state vs functions are values
- In Ada, never the twain shall meet!