I'm fine with the pure function
concept on pretty simple examples like...
function addTwo(val){
return val + 2;
}
Given the same arguments, it yields the same result, leading to Referential Transparency and good deterministic code.
But then I've came across examples like these (taken from professor frisby mostly adequate guide, but I've found similar examples on other FP JS books)
//pure
var signUp = function(Db, Email, attrs) {
return function() {
var user = saveUser(Db, attrs);
welcomeUser(Email, user);
};
};
var saveUser = function(Db, attrs) {
...
};
var welcomeUser = function(Email, user) {
...
};
and I don't get why isn't considered an external dependency (so, impure) the call to saveUser
or welcomeUser
.
I know that from a function/IO point of view, signUp
always return the "same" (an equivalent) wired function, but it feels weird to me.
It's difficult to me to understand why even
function multiplyBy(times){
return value => value * times;
}
const fiveTimes = multiplyBy(5);
fiveTimes(10);
is considered pure
. From the returned function POV, accesing to times
is a lookup on the scope-chain, it could come from the immediate outer scope, or from beyond (like global scope).
Any one wants to bring some light to this?
My explanation for function purity in JavaScript is that there's no such thing as a binary "pure" or "impure", but rather a spectrum of confidence that a function will behave predictably. There's all kinds of tricks that can be played to make a function that seems pure not be, by passing an object with a side-effect getter on it, for example.
So, once we realize that purity is about degree of confidence, we can then ask, how confident am I that some function will behave the way I expect? If that function references another function, how sure are you that the other function is pure? And, moreover, how sure are you that the identifier that's referencing that other function doesn't / can't get re-assigned to point to some other function you aren't aware of?
I personally code my programs so that I almost never re-define an identifier that's pointing at a function, especially if that function is declared rather than just a function expression. In that way, I feel very confident (say, 99.99%) that if foo(..)
calls bar(..)
, and I'm confident that bar(..)
is reliably pure, then foo(..)
is also reliably pure, because I know I won't reassign bar(..)
to any other function and cause surprising results.
Some people even go so far as to define their function identifiers with const fn = function ..
. I don't think that helps all that much... it probably would take my confidence level from 99.99% to 99.999%. That doesn't move the needle enough to justify its usage, IMO.
Moreover, on the closure part of your question: if an inner function closes over an outer variable that's still contained in a pure function, and nothing re-assigns that variable, then it's effectively a constant, so my level of confidence is very high in the predictability.
But again, the take away is that function purity in JS is about level of confidence, not an absolute binary yes or no.