Functions in Purely Functional Programming Languages are exactly like Functions in Mathematics: they produce a result value based on their argument values and only based on their argument values.
A Side-Effect (often just called Effect) is everything else. I.e. everything that is not reading the arguments and returning a result is a Side-Effect.
This includes, but is not limited to:
- mutating state,
- reading input from the console,
- printing output to the console,
- reading, creating, deleting, or writing to files,
- reading or writing from the network,
- Reflection,
- depending on the current time,
- launching or aborting a thread or process, or
- really any kind of I/O, and most importantly
- calling an impure function.
That last one is really important: calling an impure function makes a function impure. Side-Effects are infectious in this sense.
Note that saying "you are only allowed to read the arguments" is somewhat simplified. In general, we consider the environment of the function also to be kind of an "invisible" argument. That means, for example, a Closure is allowed to read variables from the environment it closes over. A function is allowed to read global variables.
Scala is an Object-Oriented Language and has Methods which have an invisible this
argument, which they are allowed to read.
The important property here is called Referential Transparency. A Function or an Expression is Referentially Transparent if you can replace it with its value without changing the meaning of the program (and vice versa).
Note that generally, the terms "Pure" or "Purely Functional", "Referentially Transparent", and "Side-Effect Free" are used interchangeably.
For example, in this following program, the (sub-)expression 2 + 3
is Referentially Transparent because I can replace it with its value 5
without changing the meaning of the program:
println(2 + 3)
has exactly the same meaning as
println(5)
However, the println
Method is not Referentially Transparent, because if I replace it with its value, the meaning of the program changes:
println(2 + 3)
does not have the same meaning as
()
Which is simply the value ()
(pronounced "unit"), which is the return value of println
.
A consequence of this is that a Referentially Transparent Function always returns the same result value when passed the same arguments. For all code, you should get the same output for the same input. Or more generally, if you do the same thing over and over, the same result should happen over and over.
And that's where the connection between loops and Side-Effects lies: a loop is doing the same thing over and over. So, it should have the same result over and over. But it doesn't: it will have a different result at least once, namely it will finish. (Unless it is an infinite loop.)
In order for loops to make sense, they must have Side-Effects. However, a Purely Functional program must not have Side-Effects. Therefore, loops cannot possibly make sense in a Purely Functional program.