let mut data = Arc::new(Mutex::new(0));
let block_size = |depth| -> usize {
// the following is not affected by
// input, nor does affect output
let v = data.lock();
*v = (*v).wrapping_add(1);
drop(v);
// dummy mapping
depth + 1
};
I.e. does "pure" refer only to input/output, or does it refer to everything within the function body?
XY: Documentation puts a "pure" constraint on a closure. Due to the nature of the closure it may be used for things like gathering statistics, which may require state [that is invisible to the caller], like a counter and a channel end-point. Reviewer argues this means it is not "pure". Original developer argues that function purity ought to refer to input/output only.
I don't know the exact definition, so I'm asking people who probably do while hoping I don't kick off a definition war.
Over the years, I've on occasion seen some wish for explicit "pure function" support in Rust, but I never really considered what this would mean exactly, until now.
Replace the concept of "purity" with "performance" or even "memory safety", and you'll get a perfectly clear idea as to why people can never seem to get along on certain aspects of this craft.
Depends fully and wholeheartedly on the definition of the term "pure" that any given proponent happens to espouse. If you're coming from Haskell, whereby any/all side effects are the devil and must be exorcised with the latest/greatest Bible of the Church of the Functional Programming; the answer will be a resounding "no". If you're more concerned with the practical aspects of "how do I log/count the number of runs for this particular function frame, without reinventing my own monoid in the category of endofunctors, from scratch?" your answer will be "meh, pure enough".
Personally: I'd define my priorities first, and picks my definitions second. Not other way around.
I think the usual definition of a "pure" function in the context of programming is the function is side effect free, in the strict sense. one property of this defintion is the function is idempotent.
but another definition is also commonly used: a pure function is a "function" as in mathematics, in that it is an abstract mapping from one set (the "domain") to another set (the "co-domain"). this definition usually emphasize the ability to memoize the function call.
from the first definition, your function is not pure, since it has side effects, it's not idempotent.
but your function might satisfy the mathematical mapping definition, but it is not strict, there's other considerations. for instance, is it acceptable if your function is skipped for some value due to caching/memoization?
so I agree with @SkiFire13 , it's not the point to argue what's the definition of the terminology. the documenation should clarify clearly what it really expects the function to do. does it only care the consistency of input and output value, or it expects the function to be idempotent so it might call it arbitrarily, such as for concurrency and parallelism optimization.
In my opinion, the function is most certainly not "pure", at least under definitions commonly used in computer science.
Lack of side effects is pretty important since it allows to perform various optimizations such as reordering and elimination of function calls if output is not used. I think it's better to call your function "deterministic", i.e. output of your function depends solely on its inputs.
The practical test is whether you care how many times the function gets called. If the result isn't used, is optimizing it out to 0 calls okay? If the function appears 2 times in the code but the program calls it once, is that okay?
In this case, I'd interpret the "pure" constraint in docs to be a non-guarantee of the number of calls or behavior if the input->output mapping changes.
Then the question is... does the resulting Mutex value require that it's a sane / non changing number of calls? Or is it merely observing behavior.
Also, does the possibility of the closure being called 1000x more than you'd expect have any side effects you'd like to avoid (deadlock or delays if sharing the Mutex among multiple such concurrent calls) if e.g. the closure-accepting function changes its calling behavior drastically across a patch version bump.