The benefit is that pure functions make your code easier to reason about. Or, in another words, side effects increase the complexity of your code.
Take an example of computeProductPrice
method.
A pure method would ask you for a product quantity, a currency, etc. You know that whenever the method is called with the same arguments, it will always produce the same result.
- You can even cache it and use the cached version.
- You can make it lazy and postpone its call to when you actually need it , knowing that the value won't change meanwhile.
- You can call the method multiple times, knowing that it won't have side effects.
- You can reason about the method itself in an isolation from the world, knowing that all it needs are the arguments.
A non-pure method will be more complex to use and debug. Since it depends on the state of the variables other than the arguments and possibly altering them, it means that it could produce different results when called multiple times, or not have the same behavior when not called at all or called too soon or too late.
Example
Imagine there is a method in the framework which parses a number:
decimal math.parse(string t)
It doesn't have referential transparency, because it depends on:
The environment variable which specifies the numbering system, that is Base 10 or something else.
The variable within the math
library which specifies the precision of numbers to parse. So with the value of 1
, parsing the string "12.3456"
will give 12.3
.
The culture, which defines the expected formatting. For instance, with fr-FR
, parsing "12.345"
will give 12345
, because the separation character should be ,
, not .
Imagine how easy or difficult would it be to work with such method. With the same input, you can have radically different results depending on the moment when you call the method, because something, somewhere changed the environment variable or switched the culture or set a different precision. The non-deterministic character of the method would lead to more bugs and more debugging nightmare. Calling math.parse("12345")
and obtaining 5349
as an answer since some parallel code was parsing octal numbers isn't nice.
How to fix this obviously broken method? By introducing referential transparency. In other words, by getting rid of global state, and moving everything to the parameters of the method:
decimal math.parse(string t, base=10, precision=20, culture=cultures.en_us)
Now that the method is pure, you know that no matter when you call the method, it will always produce the same result for the same arguments.