On Mon, Nov 17, 2025 at 02:46:06PM +0000, Brother Bill via Digitalmars-d-learn wrote: > Pure functions in Functional Programming provide the same result when > provided the same arguments. > > They are not allowed to read or write to system resources such as file > system, console, clock, network, etc. In addition, they may not > read/write to module level variables. > > In D, they are allowed to mutate parameters which seems to violate > purity. Why did D make this choice and when to best exploit this > architectural decision. > > Also, would you agree that not mutating parameters to have "true" > purity would be preferred?
The reasoning behind D's implementation of `pure` is based on the idea of external observability. The motivation behind this is to make it easier to write pure code. "True" pure code is more difficult to write, because you can't have mutation and mutable state, etc.. But sometimes, the outside world (i.e., the caller) can't observe the difference between this "dirty" code and truly pure code. From the caller's POV, then, it doesn't matter how the "pure" function is implemented inside; it may be doing all sorts of "dirty", impure stuff, but if there are no externally-observable side-effects, then it might as well have been pure as far as the caller is concerned. Thus, it can be marked `pure` and benefit from optimization opportunities afforded by pure code. This is the so-called "weak purity" in D. There's also "strong purity", which corresponds more closely to what other languages call "pure". The condition for strong purity is that the function cannot modify anything through its parameters, i.e., it cannot access the external world using a mutable reference passed to it in one of its parameters. Strong purity is desirable because it allows things like return value caching optimizations. But if we have strong purity, why bother with weak purity? The idea is that inside a strongly-pure function, it is OK to call a weakly pure function. Remember, a weakly pure function is one whose "dirty laundry" (i.e., impure side effects) are not observable to the caller. It *may* modify stuff outside its scope through references passed to it. But this isn't a problem because a strongly-pure function does not have access to anything in the outside world, so the worst it can do is to pass a mutable reference to some of its own internal state to the weakly pure function. The overall effect is still that, as far as the caller of the strongly-pure function is concerned, there are no observable external side-effects, so it is indistinguishible from a "real" pure function (the way other languages define it). The motivation behind this relaxed purity requirement is to make it easier to write a strongly-pure function. You're not restricted to calling only strongly-pure functions; you can also call weakly-pure functions because the overall difference is not observable to the caller. So more code can take advantage of purity-based optimizations. // Having said all that, though, the actual amount of purity-based optimizations in your typical D codebase is unfortunately rather small. Maybe somebody somewhere is depending on this for their performance requirements, but in practice, I'm finding that purity isn't very useful as far as optimizations are concerned. It's more useful for code organization -- knowing that a function is pure makes it easier to refactor things without breakage. Even if it's only weakly pure, the fact that any differences are not observable to the outside world is still a pretty useful property one can depend on when refactoring code. But I'd say the overall actual benefit of D's expanded definition of purity is rather minimal, and maybe not really worth bothering with. (I used to try to maximize pure usage; these days I don't bother anymore, I just templatize my code and let the compiler figure out what's pure and what's not. The optimization opportunities are limited in any case.) T -- Always remember that you are unique. Just like everybody else. -- despair.com
