You already said in your question how you can tell whether your program is referentially transparent or not: if you can replace a expression with its value, without changing the meaning of the program, it is referentially transparent. If you cannot do that, it is not.
So, let's just try and replace some expressions with their values!
in your summation
method, in the first line, you set total
to 0
. So, if your program is referentially transparent, we should be able to replace any occurrence of total
with 0
. For example, like this:
def summation(n, term):
total, k = 0, 1
while k <= n:
total, k = 0 + term(k), k + 1
return total
or like this:
def summation(n, term):
total, k = 0, 1
while k <= n:
total, k = total + term(k), k + 1
return 0
The same goes for k
:
def summation(n, term):
total, k = 0, 1
while k <= n:
total, k = total + term(k), 1 + 1
return total
or here:
def summation(n, term):
total, k = 0, 1
while k <= n:
total, k = total + term(1), k + 1
return total
or here:
def summation(n, term):
total, k = 0, 1
while 1 <= n:
total, k = total + term(k), k + 1
return total
let's put that all together:
def summation(n, term):
total, k = 0, 1
while 1 <= n:
total, k = 0 + term(1), 1 + 1
return 0
Also, what is the return value of the while
loop? The while
loop doesn't have a return value, it returns nothing. According to the rules of referential transparency, we should be able to replace any expression with its return value without changing the meaning of the program, ergo, we should be able to replace the entire while
loop with nothing:
def summation(n, term):
total, k = 0, 1
return total
Again, if your program were referentially transparent, this shouldn't change the result.
Note that a loop simply cannot possibly be referentially transparent. Referential transparency can also be interpreted as "if I do the same thing, the same thing happens". But in a loop, we do the same thing over and over again, yet, we expect something different to happen every time, or at least once: the loop stops.
Here's an example of how to implement summation
without breaking referential transparency:
def summation(n, term):
if n == 0:
return term(n)
else:
return term(n) + summation(n - 1, term)
And here is an implementation in a tail-recursive fashion using the standard "introduce an accumulator" trick:
def summation(n, term):
def summation_rec(n, total):
if n == 0:
return total
else:
return summation_rec(n-1, total + term(n))
return summation_rec(n, 0)
(Unfortunately, that won't help, since Python doesn't implement Proper Tail Calls, not even Proper Tail Recursion. So, this will still blow the stack for larger n
. But in a language with proper implementation of tail recursion or even proper tail calls, this will run in O(1) stack space.)