Purity can be measured by two things:
- Does the function always return the same output, given the same input; i.e. is it referentially transparent?
- Does the function modify anything outside of itself, i.e. does it have side-effects?
If the answer to 1 is yes and the answer to 2 is no, then the function is pure. Closures only make a function impure if you modify the closed-over variable.
You seem to be drawing a line between declaring things and instructing a machine. There is no such hard-and-fast separation. Because the machine that's being instructed in imperative programming need not be physical hardware, there is much freedom for interpretation. Almost everything can be seen as an explicit program for the right abstract machine. For example, you could see CSS as a rather high level language for programming a machine that primarily resolves selectors and sets attributes of the thus-selected DOM objects.
The question is whether such a perspective is sensible, and conversely, how closely the sequence of instructions resembles a declaration of the result being computed. For CSS, the declarative perspective is clearly more useful. For C, the imperative perspective is clearly predominant. As for languages as Haskell, well…
The language has specified, concrete semantics. That is, of course one can interpret the program as a chain of operations. It doesn't even take too much effort to choose the primitive operations so that they map nicely onto commodity hardware (this is what the STG machine and other models do).
However, the way Haskell programs are written, they frequently can sensibly be read as a description of the result to be computed. Take, for example, the program to compute the sum of the first N factorials:
sum_of_fac n = sum [product [1..i] | i <- ..n]
You can desugar this and read it as a sequence of STG operations, but far more natural is to read it as a description of the result (which is, I think, a more useful definition of declarative programming than “what to compute”): The result is the sum the products of [1..i]
for all i
= 0, …, n. And that is far more declarative than almost any C program or function.
Best Answer
Yes, it is possible, depending on the language.
In JavaScript, you can tell if a function is pure by the following criteria:
It only reads parameters and locals;
It only writes locals;
On non-locals, it calls only pure functions;
All functions it calls implicitly are pure, e.g.,
toString
; andIt only writes properties of locals if they do not alias non-locals.
Aliasing is not possible to determine in JavaScript in the general case, because you can always look up properties of an object dynamically (
object["property"]
). Provided you never do that, and you have the source of the whole program, then I think the problem is tractable. You would also need information about which native functions have side-effects, such asconsole.log
or most anything involving the DOM.The term “pure” could also use some clarification. Even in a strongly, statically typed, purely functional programming language, where all functions are referentially transparent, a function can still fail to terminate. So when we talk about
id :: a -> a
, what we’re really saying is not:But rather:
Because a valid implementation of
id
iserror "Not implemented!"
. As Peteris points out, this nontotality could be seen as a kind of impurity. Koka is a functional programming language—with syntax modelled on JavaScript—which can infer possible effects such as divergence (nontermination), referential transparency, throwing of exceptions, and I/O actions.