Pure functions

Development6 nov 2025

A little rant about functional programming and pure functions. There is a common delusion about pure functions. People often think that:

These statements make some point. But they are not entirely true. Let me clarify this. The only restrictions that pure functions have are:

What is a side effect? Basically, it’s any change in the world outside of the function. E.g.:

I think you got the concept. Now let’s focus on the word “side” in “side effects”. “Side” means it’s outside of the given function. We don’t have these restrictions for internal state. Yep, a pure function may have mutable internal state.

function pure(a: number, b: number): number {
  let c = a ** b;
  if (c > 100) c %= 100;
  return c - 14;
}

^ this one is pure, even though it mutates c. Just because c is internal to the function — it doesn’t affect or depend on anything outside. We don’t mutate it after we return the value.

Ok, what about running impure functions? It’s a dangerous zone. Because it is very dependent on how exactly impure the impure function. If it’s not deterministic, then we have no chance. So the question is about: what side effects an impure function may have while still allowing us to use it inside a pure function? The answer is: the only side effect it may have is mutating the given arguments (only if they are local to the pure function):

function impure(packet: Packet): void {
  // deterministic and based only on packet
  packet.hash = computeHash(packet); 
}

function pure(id: number, data: Data): Packet {
  const packet: Packet = { id, data };
  impure(packet);
  return packet;
}

Here we clearly run impure in pure. But since impure changes only packet, which is entirely managed by pure, we’re safe.

That’s it.

To make it clearer, here are a few examples of impure behavior:

function impure(x: { v: number }): number {
  x.v += 1; // mutates caller-visible object → impure
  return x.v;
}
let mutableValues: Record<string, Data>;

function impure(id: string) {
  return mutableValues[id].size;
}
function impure(id: number): Promise<User> {
  return fetch(`/user/${id}`);
}