UP | HOME

How is printing to the console a side effect

Functional programming favors functions that have no side effects. For a beginner, this statement represents a double conundrum.

So let's try again, but in reverse. Functional programming favors functions that, for a given input, will consistently return the same output. In algebra, a function is a notation to describe a relation. Not any type of relation, but the special case where every element that goes in (input) results in a single element getting out (output). Contrast this with equations, graphs, coordinates or ordered pairs, where such a constraint doesn't apply.

Functional programming strives to work with so-called pure functions because they allow clarity of thought. It is easy to reason about pure functions, because once defined, they are like black boxes whoses intricacies can be put aside, and you can then use them as building blocks to compute operations of ascending complexity.

The same cannot be said about functions that have side effects. Those can produce different outputs given the same input. We don't need to use a functional language to do the demonstration. Let's take print formatted, or printf, a function in C, an imperative language, used to print a string to standard output. In order to make our point, we need to show that calling the function twice with the same parameter can result in different outputs.

printf takes a string and returns the number of characters printed, or -1 when the printing was unsuccesful.

#include <stdio.h>

int main()
{
  int rc;
  rc = printf("test\n");             /* printing test string */
  fprintf(stderr, "rc: %d\n", rc);   /* return code of succesful printf call 
                                        5 in this case */
  fclose(stdout);                    /* closing standard output */
  rc = printf("test\n");             /* printing test string (don't hold your breath) */
  fprintf(stderr, "rc: %d\n", rc);   /* return code of unsuccesful prinf call 
                                        returns -1  */
}

So here is our proof: the input to printf was the string "test\n", the output was 5 when STDOUT was available, -1 after it was closed. printf is clearly not a pure function. It is dependent on the state of the console, an abstract I/O device that may or may not be ready.

Side effects, while coveted, ought to be contained, reducing thereby the area where "bad things" can happen. When you write in the functional style, you strive to separate pure functions from functions with side effects, so as not to worry about state in the same place where you write rules and logic. This will yield great benefit when reasoning about your program.

In conclusion

A function that consistently produces the same output given a same input has tremendous benefit, because now we can substitute it with its return value without affecting the outer code. In professional literature, this property is referred to as referential transparency. The ability to carry such substitutions allows for all kinds of optimizations in our programs: memoization, proofs, parallelization. And it's not hard to guess that referentially transparent functions will be easier to test, too. Some functional languages enforce referential transparency at the language level. Haskell, for example, wraps all I/O operations in a monad. Lisp languages don't (including Clojure). It is the programmer's responsiblity to ascertain that property in the functions being composed. On the other hand, when a function is used merely for its side effects, special forms may be provided for convenience purposes, like mapc, the companion to mapcar in Emacs Lisp.