Trying to summarize things that have come up in other answers and in comments:
There is only one definition of "idempotent". A function f
is idempotent if and only if f(f(x))
equals f(x)
for all x
in the domain of f
.
There is more than one definition of "equals". In a lot of contexts, we have an idea of "equivalence" which stands in for equality, and the definition of "equivalent" might be different in different contexts.
There is more than one definition of "function". In mathematics (with a conventional set-theoretic construction), a function is a set of pairs. The "domain" of the function is the set of all elements that appear in the first position of a pair. No element of the domain appears in the first position of more than one pair in the function. The "range" of the function is the set of all elements that appear in the second position of a pair. Elements of the range may appear more than once. We say that a function "maps" each element of its domain to a particular element of its range, and we write f(x)
to mean "the second element of the pair in f
that has x
as its first element".
So, it is clear that for a function to be idempotent, its range must be a subset of its domain. Otherwise, f(f(x))
is meaningless.
In computing, and particularly in imperative languages, a function is often defined as a sequence of statements/instructions, together with some named inputs and output(s) (in most languages only one output). "Calling" a function is an imperative operation that means to execute the instructions. But instructions in an imperative language can have side-effects: they can change things other than their outputs. This concept is absent from mathematics, as well as from pure functional programming.
These imperative "functions", which I will refer to from now on as "routines", can be reconciled with the mathematical definition of a function in two ways:
ignore the side-effects, and say that the routine is a function whose domain is the set of all possible combinations of argument values, and which maps those to the outputs of the routine. This stands on weak theoretical ground if the function is not "pure", that is to say if its output depends on mutable state beyond its arguments, or if it modifies state beyond its outputs. The reason is that a mathematical function by definition does not map its inputs to different outputs at different times. Nor does a mathematical function "change" things when "called", because mathematical functions aren't "called" a particular number of times. They simply "are".
incorporate the side-effects into a mathematical function that describes the effect that calling the routine has on the complete state of the machine, including the outputs from the routine but also including all global state and whatnot. This is a standard trick in CS, and it means that for every statement, instruction, call to a routine, or whatever, there is a corresponding function that maps the state of the machine before the call, to the state of the machine afterwards.
Now, if we apply the definition of "idempotent" in case 1, we are assessing whether or not the mathematical function that a particular routine was designed to implement is idempotent. If the routine does anything other than implement a mathematical function, for example if it has any side-effects, then we are on very shaky ground here, and will come up with misleading results. For example, the function int f(int i) { puts("hello!"); return i; }
could be regarded as being idempotent on the basis that "it's an implementation of the identity function!". And that's true if you ignore the side-effects, but that means the definition is useless for any practical purposes, because once side effects are taken into account, executing the expression f(f(0))
is a different thing from executing the expression f(0)
. f(f(0))
is not equivalent to f(0)
even though their return values are equal, and we could only replace one with the other if we didn't care about (that part of) the output of the program.
If we apply the definition of "idempotent" to the functions of machine states in case 2, we are assessing whether or not a call to the function (with particular arguments) is an idempotent operation on the state of the machine. Then my function f
above clearly is not idempotent -- the state of a machine with "hello!\n" written to its output device is not the same as the state of a machine with "hello!\nhello!\n" written to its output device. I think it's also clear that in this case, your function F
is idempotent (although it isn't "pure", since its return value depends on state other than its formal parameters, and so it is not simply an implementation of a mathematical function), and your function F2
is not idempotent. If test
were immutable, then we could reasonably start describing F
as pure. F2
then would be invalid.
As far as I know, when compscis talk about idempotence in imperative languages, they are usually talking about whether the functions of machine states defined by case 2, are or are not idempotent. But usage can vary -- if the routine is pure then they might talk about whether the mathematical function it represents is idempotent. In a pure functional language there is no machine state to talk about, so case 2 would be inappropriate, and any use of the term "idempotent" in relation to a function must be case 1. Functions in pure functional languages are always like mathematical functions.
F()
method is non-deterministic because it relies on an internal variable that can be modified in at least one way in your program. – Neuromuscularsqrt
's output can change if I callfesetround
. – Choongfesetround
isn't even guaranteed to actually do anything, so it's not guaranteed that it does actually change the results. But assuming an implementation that returns the correctly-rounded result from sqrt, which is permitted by the standard, then I should think it's obvious that the current rounding mode affects the results. That's what I mean when I say the output "can change". Admittedly only by 1 ulp, but that's still a different result. – Choongsqrt
without saying so. Which is a bit naughty of me in a Java question. – Choong