A pure function is one that does not cause side effects, that is, it does not change any state in the application. But not only that, it always needs to generate the same result with the same arguments, that is, it needs to be completely deterministic.
The philosophy of functional programming is that changing states cause problems. And it’s true, they do. Of course, always having states that don’t change creates other problems. Note that it is virtually impossible to create an application that does something useful without some status change.
If the result of a function is always as expected it is easier to handle that function.
So its use has to do with facilitating development, even if it causes, eventually, a loss of performance. So the most pragmatic languages only use immutability when really useful and does not bring other problems.
One of the difficulties of an impure function is that it can only be used in other impure functions since an impure contaminates the pure.
When the function only generates a deterministic result and does not change state it is easier to understand its functioning, the operation flow is more predictable, it is easier to debug and test the code, it is necessary to debug less code since it tends to have fewer errors, it is much easier to deal with competition and parallelization, and it is easier to do complex things given the simplicity of it, which even allows aggressive optimizations.
Contrary to what many people think is not the algorithm that is difficult to deal with is the data structure. It is it that always gives problem. It is not the behavior but the state. Unless the algorithm is very complex and poorly done.
So a lot of people think OOP is the eighth wonder of the world. It tends to facilitate the best data structure (although most people misunderstand and more difficult to facilitate, and that is not well OOP but modularization that makes it occur), except that OOP still preaches the state mutation, preaches that the behavior alters the state.
Functionalists consider OOP to be crap because the state changes so much. Pragmatists know when to use everything. And for that you have to understand what you’re doing, which is not easy because there is no cake recipe and there is a lot of information that needs to be combined to make the right decision.
So we should prefer the state that doesn’t change whenever it doesn’t cause other problems, performance problems for example. We should prefer behaviors that do not change state and that do not use states that can change, these behaviors are pure.
A function that accesses something external to the application cannot be pure. Any data input (read keyboard, access storage, receive network packet, access another application, ask for something for the operating system like clock, etc.) or any calculation that depends on the state of something that the application does not control and cannot guarantee that the state is always the same (true random generation is the greatest example of something that must give a result that depends on a nondeterministic state, has a random function that is deterministic, although it needs to be used specifically to be pure). To help deal with this functional languages have monads.
Mathematical functions in general are pure. What works with types by value passed by value is usually pure, what works on something by reference that is an immutable object is usually pure.
Academically it is customary to assign these characteristics to a pure function:
Whenever I find some more mathematical question, I quote the definition of pure function by quoting this question. I’ve lost count of how many times I’ve done it
– Jefferson Quesado