A function is deterministic if it returns an equivilant value when provided equivilant arguments. Pure functions are always deterministic, but determinism may be achieved without purity. In particular, an unpure deterministic function may still have side effects, as long as those side effects don’t change future invocations.
Determinism may be conditional on some rule. A method on an object might be deterministic, but only for calls on the same instance. A function may also only be deterministic for some prescribed amount of time, like a single update step in a game engine. The most common distinction is that of compile time vs runtime determinism. Compile time determinism is the standard, where results could be predicted before the program even starts. Runtime determinism only guarentees that the results of the function won’t change while the program is running, such that code may be written to rely on results not changing.
The easiest way to ensure runtime determinism is to avoid reading from mutable state. A function which may only access immutable values to compute its result, and cannot access Information, like the clock, will be deterministic. The lack of access to any changing data outside the arguments will prevent changes to the computed result.
Importantly, a deterministic function may still read mutable data, but since the data cannot be used to compute the result, deterministic functions may only use it for either a performance boost, or in the invocation of side effects. A common example of this is with Memoization, where a function caches its results using the arguments as an identifier. A memoized function would check the cache, reading mutable data, and update the cache if the value is missing, writing mutable data.