Most sources define a pure function as having the following two properties:
It is the first condition that concerns me. In most cases, it's easy to judge. Consider the following JavaScript functions (as shown in this article)
Pure:
const add = (x, y) => x + y;
add(2, 4); // 6
Impure:
let x = 2;
const add = (y) => {
return x += y;
};
add(4); // x === 6 (the first time)
add(4); // x === 10 (the second time)
It's easy to see that the 2nd function will give different outputs for subsequent calls, thereby violating the first condition. And hence, it's impure.
This part I get.
Now, for my question, consider this function which converts a given amount in dollars to euros:
(EDIT - Using const in the first line. Used let earlier inadvertently.)
const exchangeRate = fetchFromDatabase(); // evaluates to say 0.9 for today;
const dollarToEuro = (x) => {
return x * exchangeRate;
};
dollarToEuro(100) //90 today
dollarToEuro(100) //something else tomorrow
Assume we fetch the exchange rate from a db and it changes every day.
Now, no matter how many times I call this function today, it will give me the same output for the input 100. However, it might give me a different output tomorrow. I'm not sure if this violates the first condition or not.
IOW, the function itself doesn't contain any logic to mutate the input, but it relies on an external constant that might change in the future. In this case, it's absolutely certain it will change daily. In other cases, it might happen; it might not.
Can we call such functions pure functions. If the answer is NO, how then can we refactor it to be one?
The dollarToEuro's return value depends on an outside variable that is not an argument; therefore, the function is impure.
If the answer is NO, how then can we refactor the function to be pure?
One option is to pass in exchangeRate. This way, every time arguments are (something, somethingElse), the output is guaranteed to be something * somethingElse:
const exchangeRate = fetchFromDatabase(); // evaluates to say 0.9 for today;
const dollarToEuro = (x, exchangeRate) => {
return x * exchangeRate;
};
Note that for functional programming, you should avoid let - always use const to avoid reassignment.
An answer of a me-purist (where "me" is literally me, since I think this question does not have a single formal "right" answer):
In a such dynamic language as JS with so many possibilities to monkey patch base types, or make up custom types using features like Object.prototype.valueOf it's impossible to tell whether a function is pure just by looking at it, since it's up to the caller on whether they want to produce side effects.
A demo:
const add = (x, y) => x + y;
function myNumber(n) { this.n = n; };
myNumber.prototype.valueOf = function() {
console.log('impure'); return this.n;
};
const n = new myNumber(42);
add(n, 1); // this call produces a side effect
An answer of me-pragmatist:
From the very definition from wikipedia
In computer programming, a pure function is a function that has the following properties:
- Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).
- Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).
In other words, it only matters how a function behaves, not how it's implemented. And as long as a particular function holds these 2 properties - it's pure regardless how exactly it was implemented.
Now to your function:
const exchangeRate = fetchFromDatabase(); // evaluates to say 0.9 for today;
const dollarToEuro = (x, exchangeRate) => {
return x * exchangeRate;
};
It's impure because it does not qualify the requirement 2: it depends on the IO transitively.
I agree the statement above is wrong, see the other answer for details: https://stackoverflow.com/a/58749249/251311
Other relevant resources: