javascript - with - pure functions react




Is this a pure function? (7)

Can we call such functions pure functions. If the answer is NO, how then can we refactor it to be one?

As you duly noted, "it might give me a different output tomorrow" . Should that be the case, the answer would a resounding "no" . This is especially so if your intended behaviour of dollarToEuro has been correctly interpreted as:

const dollarToEuro = (x) => {
  const exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;
  return x * exchangeRate;
};

However, a different interpretation exists, where it would be considered pure:

const dollarToEuro = ( () => {
    const exchangeRate =  fetchFromDatabase();

    return ( x ) => x * exchangeRate;
} )();

dollarToEuro directly above is pure.

From a software engineering perspective, it's essential to declare the dependency of dollarToEuro on the function fetchFromDatabase . Therefore, refactor the definition of dollarToEuro as follows:

const dollarToEuro = ( x, fetchFromDatabase ) => {
  return x * fetchFromDatabase();
};

With this outcome, given the premise that fetchFromDatabase functions satisfactorily, then we can conclude that the projection of fetchFromDatabase on dollarToEuro must be satisfactory. Or the statement " fetchFromDatabase is pure" implies dollarToEuro is pure (since fetchFromDatabase is a basis for dollarToEuro by the scalar factor of x .

From the original post, I can understand that fetchFromDatabase is a function time. Let's improve the refactoring effort to make that understanding transparent, hence clearly qualifying fetchFromDatabase as a pure function:

fetchFromDatabase = ( timestamp ) => { /* here goes the implementation */ };

Ultimately, I would refactor the feature as follows:

const fetchFromDatabase = ( timestamp ) => { /* here goes the implementation */ };

// Do a partial application of `fetchFromDatabase` 
const exchangeRate = fetchFromDatabase.bind( null, Date.now() );

const dollarToEuro = ( dollarAmount, exchangeRate ) => dollarAmount * exchangeRate();

Consequently, dollarToEuro can be unit-tested by simply proving that it correctly calls fetchFromDatabase (or its derivative exchangeRate ).

Most sources define a pure function as having the following two properties:

  1. Its return value is the same for the same arguments.
  2. Its evaluation has no side effects.

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?


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 sources

In computer programming, a pure function is a function that has the following properties:

  1. 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).
  2. 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://.com/a/58749249/251311

Other relevant resources:


As written, it is a pure function. It produces no side effects. The function has one formal parameter, but it has two inputs, and will always output the same value for any two inputs.


I’d like to back out a bit from the specific details of JS and the abstraction of formal definitions, and talk about which conditions need to hold to enable specific optimizations. That’s usually the main thing we care about when writing code (although it helps prove correctness, too). Functional programming is neither a guide to the latest fashions nor a monastic vow of self-denial. It is a tool to solve problems.

When you have code like this:

let exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x) => {
  return x * exchangeRate;
};

dollarToEuro(100) //90 today

dollarToEuro(100) //something else tomorrow

If exchangeRate could never be modified in between the two calls to dollarToEuro(100) , it is possible to memo-ize the result of the first call to dollarToEuro(100) and optimize away the second call. The result will be the same, so we can just remember the value from before.

The exchangeRate might be set once, before calling any function that looks it up, and never modified. Less restrictively, you might have code that looks up the exchangeRate once for a particular function or block of code, and uses the same exchange rate consistently within that scope. Or, if only this thread can modify the database, you would be entitled to assume that, if you did not update the exchange rate, no one else has changed it on you.

If fetchFromDatabase() is itself a pure function evaluating to a constant, and exchangeRate is immutable, we could fold this constant all the way through the calculation. A compiler that knows this to be the case could make the same deduction you did in the comment, that dollarToEuro(100) evaluates to 90.0, and replace the entire expression with the constant 90.0.

However, if fetchFromDatabase() does not perform I/O, which is considered a side-effect, its name violates the Principle of Least Astonishment.


Technically, any program that you execute on a computer is impure because it eventually compiles down to instructions like “move this value into eax ” and “add this value to the contents of eax ”, which are impure. That's not very helpful.

Instead, we think about purity using black boxes . If some code always produces the same outputs when given the same inputs then it's considered to be pure. By this definition, the following function is also pure even though internally it uses an impure memo table.

const fib = (() => {
    const memo = [0, 1];

    return n => {
      if (n >= memo.length) memo[n] = fib(n - 1) + fib(n - 2);
      return memo[n];
    };
})();

console.log(fib(100));

We don't care about the internals because we are using a black box methodology for checking for purity. Similarly, we don't care that all code is eventually converted to impure machine instructions because we're thinking about purity using a black box methodology. Internals are not important.

Now, consider the following function.

const greet = name => {
    console.log("Hello %s!", name);
};

greet("World");
greet("Snowman");

Is the greet function pure or impure? By our black box methodology, if we give it the same input (e.g. World ) then it always prints the same output to the screen (i.e. Hello World! ). In that sense, isn't it pure? No, it's not. The reason it's not pure is because we consider printing something to the screen a side effect. If our black box produces side effects then it is not pure.

What is a side effect? This is where the concept of referential transparency is useful. If a function is referentially transparent then we can always replace applications of that function with their results. Note that this is not the same as function inlining .

In function inlining, we replace applications of a function with the body of the function without altering the semantics of the program. However, a referentially transparent function can always be replaced with its return value without altering the semantics of the program. Consider the following example.

console.log("Hello %s!", "World");
console.log("Hello %s!", "Snowman");

Here, we inlined the definition of greet and it didn't change the semantics of the program.

Now, consider the following program.

undefined;
undefined;

Here, we replaced the applications of the greet function with their return values and it did change the semantics of the program. We are no longer printing greetings to the screen. That's the reason why printing is considered a side effect, and that's why the greet function is impure. It's not referentially transparent.

Now, let's consider another example. Consider the following program.

const main = async () => {
    const response = await fetch("https://time.akamai.com/");
    const serverTime = 1000 * await response.json();
    const timeDiff = time => time - serverTime;
    console.log("%d ms", timeDiff(Date.now()));
};

main();

Clearly, the main function is impure. However, is the timeDiff function pure or impure? Although it depends upon serverTime which comes from an impure network call, it is still referentially transparent because it returns the same outputs for the same inputs and because it doesn't have any side effects.

zerkms will probably disagree with me on this point. In his answer , he said that the dollarToEuro function in the following example is impure because “it depends upon the IO transitively.”

const exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x, exchangeRate) => {
  return x * exchangeRate;
};

I have to disagree with him because the fact that the exchangeRate came from a database is irrelevant. It's an internal detail and our black box methodology for determining the purity of a function doesn't care about internal details.

In purely functional languages like Haskell, we have an escape hatch for executing arbitrary IO effects. It's called unsafePerformIO , and as the name implies if you do not use it correctly then it's not safe because it might break referential transparency. However, if you do know what you're doing then it's perfectly safe to use.

It's generally used for loading data from configuration files near the beginning of the program. Loading data from config files is an impure IO operation. However, we don't want to be burdened by passing the data as inputs to every function. Hence, if we use unsafePerformIO then we can load the data at the top level and all our pure functions can depend upon the immutable global config data.

Note that just because a function depends upon some data loaded from a config file, a database, or a network call, doesn't mean that the function is impure.

However, let's consider your original example which has different semantics.

let exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x) => {
  return x * exchangeRate;
};

dollarToEuro(100) //90 today

dollarToEuro(100) //something else tomorrow

Here, I'm assuming that because exchangeRate is not defined as const , it's going to be modified while the program is running. If that's the case then dollarToEuro is definitely an impure function because when the exchangeRate is modified, it'll break referential transparency.

However, if the exchangeRate variable is not modified and will never be modified in the future (i.e. if it's a constant value), then even though it's defined as let , it won't break referential transparency. In that case, dollarToEuro is indeed a pure function.

Note that the value of exchangeRate can change every time you run the program again and it won't break referential transparency. It only breaks referential transparency if it changes while the program is running.

For example, if you run my timeDiff example multiple times then you'll get different values for serverTime and therefore different results. However, because the value of serverTime never changes while the program is running, the timeDiff function is pure.


The dollarToEuro 's return value depends on an outside variable that's not an argument, so it's impure.

In the answer is NO, how then can we refactor it to be one?

One option would be to pass in exchangeRate . That way, every time the arguments are (something, somethingElse) , the output is guaranteed to be exactly 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 definitely avoid let too - always use const , so as to avoid reassignment.


To expand on the points others have made about referential transparency: we can define purity as simply being referential transparency of function calls (i.e. every call to the function can be replaced by the return value without changing the semantics of the program).

The two properties you give are both consequences of referential transparency. For example, the following function f1 is impure, since it doesn't give the same result every time (the property you've numbered 1):

function f1(x, y) {
  if (Math.random() > 0.5) { return x; }
  return y;
}

Why is it important to get the same result every time? Because getting different results is one way for a function call to have different semantics from a value, and hence break referential transparency.

Let's say we write the code f1("hello", "world") , we run it and get the return value "hello" . If we do a find/replace of every call f1("hello", "world") and replace them with "hello" we will have changed the semantics of the program (all of the calls will now be replaced by "hello" , but originally about half of them would have evaluated to "world" ). Hence calls to f1 are not referentially transparent, hence f1 is impure.

Another way that a function call can have different semantics to a value is by executing statements. For example:

function f2(x) {
  console.log("foo");
  return x;
}

The return value of f2("bar") will always be "bar" , but the semantics of the value "bar" are different from the call f2("bar") since the latter will also log to the console. Replacing one with the other would change the semantics of the program, so it's not referentially transparent, and hence f2 is impure.

Whether your dollarToEuro function is referentially transparent (and hence pure) depends on two things:

  • The 'scope' of what we consider referentially transparent
  • Whether the exchangeRate will ever change within that 'scope'

There is no "best" scope to use; normally we would think about a single run of the program, or the lifetime of the project. As an analogy, imagine that every function's return values get cached (like the memo table in the example given by @aadit-m-shah): when would we need to clear the cache, to guarantee that stale values won't interfere with our semantics?

If exchangeRate were using var then it could change between each call to dollarToEuro ; we would need to clear any cached results between each call, so there would be no referential transparency to speak of.

By using const we're expanding the 'scope' to a run of the program: it would be safe to cache return values of dollarToEuro until the program finishes. We could imagine using a macro (in a language like Lisp) to replace function calls with their return values. This amount of purity is common for things like configuration values, commandline options, or unique IDs. If we limit ourselves to thinking about one run of the program then we get most of the benefits of purity, but we have to be careful across runs (e.g. saving data to a file, then loading it in another run). I wouldn't call such functions "pure" in an abstract sense (e.g. if I were writing a dictionary definition), but have no problem with treating them as pure in context .

If we treat the lifetime of the project as our 'scope' then we're the "most referentially transparent" and hence the "most pure", even in an abstract sense. We would never need to clear our hypothetical cache. We could even do this "caching" by directly rewriting the source code on disk, to replace calls with their return values. This would even work across projects, e.g. we could imagine an online database of functions and their return values, where anyone can look up a function call and (if it's in the DB) use the return value provided by someone on the other side of the world who used an identical function years ago on a different project.





functional-programming