Purity Is a Confidence Level, Not a Yes or No
Why asking whether a function call is pure is the wrong question, and why the right answer is always a degree of confidence, not yes or no.
Last time, pure meant a strict, provable guarantee: same input, same output, every single time. That definition is real. It's also not quite how you'll use this in practice.
Here's the question I kept asking myself while checking real code: is this function call pure, yes or no?
That's the wrong question.
Confidence, Not a Verdict
getPrice(item) from the last post was either fully pure or completely unpredictable, and it hinged on one thing: whether .price was a plain value or a getter with hidden randomness in it. Most real code doesn't hand you that answer so cleanly.
Can you see every line that touches item? Are you sure nothing reassigns it three files away? Do you actually know what a library function does internally, or are you trusting its name? The call is what actually matters here, and confidence in a call is exactly the kind of thing that comes in degrees.
Every one of those questions has a shade of an answer, not a flat yes or no. Purity in practice is exactly that: how confident are you, right now, given what you can actually see?
Why This Isn't a Cop-Out
This might sound like a weaker, mushier version of the rule from the last post. It isn't.
The strict definition still exists, and it's still the target. What changes is how you talk about the code in front of you, since you almost never have perfect visibility into every line that could affect it.
Say the word "pure" about a function call, and you're really making a claim about your own confidence in it, not a mathematical fact you've fully verified. The last post's makeDiscounter example earns a very high confidence, close to certainty, because its whole surface area was one small function. A function calling into a third-party library you've never read the source of earns a lower one, even if it happens to behave purely every time you've tested it.
What This Buys You
Once you stop expecting a binary answer, you get a genuinely useful habit instead: sort every function call in your code into high confidence and low confidence, and work to shrink the second pile.
That's the whole practical core of writing functional code. Not "never write an impure function." Shift as much of your codebase as you can toward the calls you'd stake real confidence on, and know exactly where the rest live.
That's also the real reason side effects need naming and isolating, not banning outright. You can't always shrink the low-confidence pile to zero, but you can always make it obvious which pile a given line belongs to. That's what's worth figuring out next.
Keep reading