I am confused about the notion of immutability. Consider the following structure of a simplistic calculator:
Here are the interfaces:
interface IOperationalInterface {
int Sum(int a, int b);
}
interface IAuditInterface {
int SumInvocations();
}
IOperationalInterface.Sum
should compute the sum of two integers and IAuditInterface.SumInvocations
should return the total number of received Sum
calls.
Here is a trivial implementation:
class CalculatorImpl : IOperationalInterface, IAuditInterface {
private int invocations = 0;
public int Sum(int a, int b) {
invocations++;
return a + b;
}
public int SumInvocations() {
return invocations;
}
}
Is CalculatorImpl
immutable? Obviously not, because its state changes with each invocation of the Sum
method. Is the Sum
operation pure? According to Wikipedia it is not, since it changes the state of a mutable object, and that side-effect is observable through the IAuditInterface
. Obviously the SumInvocations
operation is also not pure, since it may return different results.
In summary, CalculatorImpl
is mutable and all of its methods are impure.
However, from the viewpoint of CalculatorClient
, who talks to it only through the IOperationalInterface
, it appears to be immutable and the Sum
operation appears to be pure in the sense that it could not observe any side effects through that interface.
On the other hand, from the perspective of AuditClient
, it is completely different: it is obvious that the object implementing the AuditInterface
is mutable and its SumInvocations
operation is impure, and this follows directly from the specification of the IAuditInterface
.
So, it is possible to partition an interface/specification of a mutable class such that some parts of it will appear mutable and some not. In this case, considering only the Sum
operation and leaving out the requirement that the invocations should be counted, we get something that does not have any side-effects.
Now, on implementing the CalculatorClient
one can take into account the fact that the object behind the interface appears to be immutable. At least one could not tell the difference.
So my question is: does it make sense to talk about "immutability" of interfaces or is it a bad idea? How else can I communicate the fact that there will be no observable side-effects through that interface? And if it's bad, what could go wrong?
UPDATE
Thanks for your answers/comments! I see now that there is no way to say that the IOperationalInterface
is pure; the conditions of purity are much too strong to apply in this case. However the question remains whether there is a weaker notion (maybe "immutability"?) which is applicable.
Best Answer
Whatever you can observe through the interface is irrelevant to the concept of purity. If
Sum
logged results to a file, it would still be impure even though you wouldn't be able to observe any changes through the interface.Purity is a very strong condition. If you say that something is pure, I'll take you at your word and assume nothing bad will happen if I run it from multiple threads, cache the results and only call it once for any given set of inputs, or that I can call it 1,000,000 times without exhausting the OS's file handles. Those assumptions can backfire when using
CalculatorImpl
.There's nothing wrong with partitioning the interfaces the way you have, but you should be precise. The specification you want for
IOperationalInterface
is probably closer to: "The return value of these methods must depend only on their arguments, but their execution may have side effects." You can be more specific about the side effects and say that the methods must not perform any kind of I/O. But I wouldn't say that it's pure, because you're very clearly not using it that way.