[swift-evolution] [Pitch] Support for pure functions. Part n + 1.
michel.fortin at michelf.ca
Sun Feb 19 13:47:59 CST 2017
I'm a bit disappointed to see this discussion bikeshedding the syntax without clarifying much the semantics. Here's a few question of semantic nature.
The base principle of a pure function is that it has no side effects. But it's quite clear that at least some side effects will need to be allowed. So which ones?
1. Many basic calculations will leave flags in registers that persist until the next computation. Those can be checked by the program later (after the function is run in some cases). That's probably a side effect you want to ignore.
2. Floating point operations can give different results depending on flags you can set in the registers. Things like setting the rounding mode for instance. Will the pure function need to reset those flags before performing floating point operations so it can guaranty the same result every time? What are the implication if those are ignored?
3. Is a pure function allowed to dereference pointers or object references passed as parameters? A pointer or an object reference might provide access to the global state of the program.
4. Allocating memory requires access to the global memory layout of the program. And allocating will give you a different memory address every time. Are you allowed to allocate memory in a pure function?
5. Many basic operations in the language will implicitly allocate memory or dereference pointers. Same for the containers in the standard library. If you don't allow memory allocations or pointer dereferencing, what subset of the language is still usable in a pure function?
6. If you do allow memory allocations inside the function, is it safe to instantiate and return a new class?
7. Is it desirable that the optimizer sometime take the pure attribute to heart to combine multiple apparently redundant calls into a single one? Or is pure not intended to be usable for compiler optimizations? The ability to optimize will likely be affected by the answer to these question and the loopholes you are willing to allow.
8. Is exiting the program (`fatalError`, etc.) allowed? That's a pretty big side effect. Although it could also be considered as no side effect since the program cannot go further.
9. Is throwing allowed? Maybe the thrown error should considered simply as different return value.
10. Is += allowed inside a pure function? Operators are functions too, but can += be made pure with `inout` and no return value?
11. Can you use a BigInt implementation in a pure function? BigInt needs to allocate internally.
13. Say you want to keep logs of what happens in the function in order to debug something: is there an "unsafe" way to do IO for that purpose? Or maybe you want to implement `fatalError` as a pure function: is there a way to print something before exiting?
Basically, there is no such thing as "no side effect". You need to pick which side effects are acceptable and then evaluate where your picks puts you on the scale between too lax (and of little utility) and too restrictive (and not usable outside of trivial examples). It might be a good idea to make the answer to all those questions clear in the proposal so people have an idea of what they can expect pure functions to do and what guaranties they provide.
More information about the swift-evolution