最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why this implementation of a pure function isn't considered to have external dependencies? - Stack Overflow

programmeradmin11浏览0评论

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?

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?

Share Improve this question asked Oct 3, 2016 at 14:50 sminutolisminutoli 8414 silver badges11 bronze badges 6
  • 1 How could times come from beyond the immediate outer scope? – user1106925 Commented Oct 3, 2016 at 15:01
  • 1 Suppose it were const multiply = ... (ES2015). Would you still have a question about whether fiveTimes was pure? JavaScript isn't primarily a functional language (although it can mostly be used that way), so if the mutability of those identifiers is what's bothering you, the answer might be "by convention until we can use ES2015 we're assuming you leave function identifiers unchanged." – T.J. Crowder Commented Oct 3, 2016 at 15:10
  • @squint in my snippet is no other way, but what i'm trying to say (I'm not a native english speaker) is that, if you take the returned function in isolation and examine the body (value * times) times isn't present on the signature, so it grabs from the scope via lookup on the scope-chain. Therefore it seems pretty impure. – sminutoli Commented Oct 3, 2016 at 15:16
  • 2 I see what you're getting at. By design it's pure, but there's no language/implementation level enforcement. It's not in isolation, however if you didn't see the full code, you'd not know that and would need to "trust" a claim of purity. Doesn't make it less of a pure function; it's just that your guarantees come from elsewhere. – user1106925 Commented Oct 3, 2016 at 15:21
  • 2 In functional speak, the signature of multiplyBy would be something like multiplyBy:: a -> b -> c; multiplyBy takes an argument a and returns a function which takes an argument b and returns a value c. The second one, function which takes a b, is a type in itself. multiplyBy creates a new type, a function b => b * 5. That's pretty darn pure. – deceze Commented Oct 3, 2016 at 15:23
 |  Show 1 more comment

2 Answers 2

Reset to default 17

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.

The definition of a pure function is:

A function that does always evaluates the same result value given the same argument value(s) and that does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.

"Having dependencies" plays absolutely no role in defining a pure function. The only thing that matters is whether the function has any side effects and whether its result depends on external state. If a function behaves exactly the same and always produces the same result given the same input, it is pure. If a function has any side effects (modifies global state) or behaves differently depending on external state, it is impure. A function may have a dependency on (read: call) another function as part of its operation; as long as that other function is also pure, that doesn't taint the purity.

The signUp example you show is pure, because it only acts on its input and always returns the exact same output when called with the same input. Note that the function itself doesn't "do" anything. It's not calling the database or produce any side effect. All it does is return a function. But this returned function is always the same if the input is the same. That returned function is in effect impure; but the function that produced it is not.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论