Side Effects

A "side effect" is, roughly speaking, a change in the status of a program caused by the actions of a subprogram that can not be anticipated by a casual reading of the subprogram's header or a statement that calls upon the subprogram. Side effects are undesirable, because their presence complicates understanding of the totality of a program's actions, and they work against software reusability, a major goal of object oriented programming (OOP). Further, they are almost always easily avoided.

Unfortunately, a mature culture of programming in languages such as C++ and Visual Basic has arisen in which, in many circles, it's considered alright to use side effects. I prefer to discourage them. You are hereby warned that side effects will be considered negatively when I grade your projects.

One source of side effects is the usage of a variable "globally." This refers to the use of a variable in a subprogram that neither appears in the subprogram's list of parameters nor in its local variables. Since the variable is not used as an actual parameter by the statement that calls upon the subprogram using the variable globally, a change in the value of the variable by the actions of the subprogram cannot be anticipated by a casual reading of either the calling statement or the function header. Such a side effect is easily avoided by passing the information shared by the caller and the subprogram through the subprogram's parameter list. Unfortunately, this issue is made murky by techniques of OOP, since a member of a class is often used globally within the code of the class. Still, we can recommend that a quantity that need not be a member of a class should be declared locally within the code of the first subprogram that uses it; if the value of such a variable is to be shared with other subprograms, then the sharing should be done via parameter lists.

Other ways in which side effects may appear are associated with C++ functions that are not declared "void" (equivalently, Visual Basic Functions) -- such a function is expected to return a value, so that it is a "function" in the mathematical sense, namely, producing exactly one new data value for its caller that is completely determined by the values of the actual parameters in the calling statement's parameter list. Thus, if either more than one new data value is produced (for example, by a change in the value of a parameter passed by reference in addition to the function's return value), or if output is produced (which represents a kind of new data), or if the action of the function is not completely determined by its parameter list (e.g., perhaps input executed during the function's execution helps determine the function's value), our understanding of "function" in the mathematical sense is violated; hence, a casual reading of the function header misleads us as to the type of effects we should expect the function to have on its caller.

It follows from the remarks above that, in a C++ function that is not "void" or in a Visual Basic Function, all of the following represent side effects:

  • input (whether from the keyboard, or from an external file), because the presence of input means the value of the function is (likely) not determined completely by its parameter list (note: the old curmudgeon will allow an exception for the main function of a C++ program, which typically has type int and generally should be expected to have some input);

  • output (whether to the screen or to an external file), because it represents an effect of the function not anticipated by the function header, and a kind of new data beyond the value returned as the value of the function (as above, an exception is made for the main function of a C++ program);

  • a parameter passed by reference/address/pointer, because the presence of such a parameter represents another new data value (besides the value of the function itself) to be returned to the caller, in violation of our expectation that the "function" will be a function in the mathematical sense. Here, of course, we have a problem for C++ programmers, because C++ treats the name of an array as a pointer: we certainly want to be able to use "int" or "float" valued functions of arrays for such purposes as totaling, averaging, searching, and many other purposes. Let us use the guideline, therefore, that a non-void function may pass parameters by pointer when necessary or desirable, but will be regarded as having a side effect if it sends back to its caller a new data value for such a parameter or for the data to which the pointer points.

Each of the latter group of side effects is easily avoided by making the subprogram in question a "void" function in C++, or a Sub in Visual Basic, and by letting the value we had intended to pass as the value of the function be, instead, a parameter passed by reference. A void function in C++, or a Sub in Visual Basic, is not expected to act as a mathematical function, hence may (without surprise) pass multiple parameters by reference and perform i/o. (It still should not use a variable globally.)