Referential transparency
To understand referential transparency, let's first consider the following description:
Let me tell you a bit about India's capital, New Delhi. The Indian capital houses the Indian Parliament. The Indian capital is also home to Gali Paranthe Wali, where you get to eat the famous parathas.
We can also say the following instead:
Let me tell you a bit about India's capital, New Delhi. New Delhi houses the Indian Parliament. New Delhi is also home to Gali Paranthe Wali, where you get to eat the famous parathas.
Here, we substituted New Delhi with the Indian capital, but the meaning did not change. This is how we would generally express ourselves.
The description is referentially transparent with the following commands:
scala> def f1(x: Int, y: Int) = x * y f1: (x: Int, y: Int)Int scala> def f(x: Int, y: Int, p: Int, q: Int)= x * y + p * q f: (x: Int, y: Int, p: Int, q: Int)Int scala> f(2, 3, 4, 5) res0: Int = 26
If we rewrite the f
method as follows, the meaning won't change:
scala> def f(x: Int, y: Int, p: Int, q: Int)= f1(x, y) + f1(p, q) f: (x: Int, y: Int, p: Int, q: Int)Int
The f1
method just depends upon its arguments, that is, it is pure.
Which method is not referentially transparent? Before we look at an example, let's look at Scala's ListBuffer
function:
scala> import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
The ListBuffer
is a mutable collection. You can append a value to the buffer and modify it in place:
scala> val v = ListBuffer.empty[String] v: scala.collection.mutable.ListBuffer[String] = ListBuffer() scala> v += "hello" res10: v.type = ListBuffer(hello) scala> v res11: scala.collection.mutable.ListBuffer[String] = ListBuffer(hello) scala> v += "world" res12: v.type = ListBuffer(hello, world) scala> v res13: scala.collection.mutable.ListBuffer[String] = ListBuffer(hello, world)
Armed with this knowledge, let's now look at the following command:
scala> val lb = ListBuffer(1, 2) lb: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2) scala> val x = lb += 9 x: lb.type = ListBuffer(1, 2, 9) scala> println(x.mkString("-")) 1-2-9 scala> println(x.mkString("-")) 1-2-9
However, by substituting x
with the expression (lb += 9)
, we get the following:
scala> println((lb += 9).mkString("-")) // 1 1-2-9-9 scala> println((lb += 9).mkString("-")) // 2 1-2-9-9-9
This substitution gave us different results. The +=
method of ListBuffer
is not a pure function as there is a side effect that occurred. The value of the lb
variable at 1
and 2
is not the same.