FunHop: Understanding Referential Transparency


In this episode of FunHop, we would try to look at what is Referential Transparency, what is substitution model and how being immutable really helps.

Referential transparency means that we can exchange the expression by its value or a value by the expression and nothing changes. Pure functions are referentially transparent. Any function f(a)=b is a pure function if for the same “a” we end up getting the same “b” without any internal or external state change.

For example the + function is a pure function
2 + 3 and

val c= 2
c + 3

would always yield the same result. There are no side effects either.

Now, let us look at Referential Transparency.

Loot at the code block below

val x = 7
val c = x + 2
val d = c + 3

Now, the value of c remains 9 even if we substitue the expression with the value of x which is 7. Taking it a step further, lets look at the following code

val x=7
val c= 7 + 2, and
val d = 7 + 2 + 3

As you would agree, the results would remain the same.
Hence, if the expression can be replaced by its value and vice versa AND nothing changes then it is called Referential Transparency. This model of substituting values for expressions is called Substitution Model.

Substitution model gives us a way to naturally reason code. You would observe that substitution model lends itself easily to immutability. Only when objects are immutable, we can substitute them without any concerns on the results. Ok, let us look at a more concrete example

For example, let us refer to a money class which is represented like this

case class Money(var amt: Int) {
  def add(anotherAmt: Int): Money = {
    this.amt += anotherAmt
    this
  }

Aha! some weirdness here, the add method mutates the money object and returns the mutated object. Now, for the following block of code

object RefTrans extends App {
  val money1 = new Money(10)
  val money2 = money1.add(20)
  
  val sum1 = money2.add(10)
  println(s"Your sum1 is $sum1")
  val sum2 = money2.add(10)
  println(s"Your sum2 is $sum2")
 }

The output is
Your sum1 is Money(40)
Your sum2 is Money(50)

Now going by the Referential Transparency and Substitution model, let us replace money2 by the expression for money2 which is money1.add(20)
Hence our code becomes

object RefTrans extends App {
  val money1 = new Money(10)
  val money2 = money1.add(20)
  
  val sum1 = money1.add(20).add(10)
  println(s"Your sum1 is $sum1")
  val sum2 = money1.add(20).add(10)
  println(s"Your sum2 is $sum2")

}

Now the output becomes
Your sum1 is Money(60)
Your sum2 is Money(90)

Weird Huh? 😉

This is different than what we got first. This shows that money2 expression is NOT referentially transparent. We cannot substitute the expression with its values and get the same result.

Ok, now let us change the code a little and have GoodMoney instead which DOES NOT mutate the object.

case class GoodMoney(val amt: Int) {
  def add(anotherAmt: Int): GoodMoney = GoodMoney(this.amt + anotherAmt)
}

Now let us start the same cycle with GoodMoney

object RefTrans extends App {
  val money1 = new GoodMoney(10)
  val money2 = money1.add(20)
  
  val sum1 = money2.add(10)
  println(s"Your sum1 is $sum1")
  val sum2 = money2.add(10)
  println(s"Your sum2 is $sum2")

}

The output is
Your sum1 is GoodMoney(40)
Your sum2 is GoodMoney(40)

And now let us do the substitution,

object RefTrans extends App {
  val money1 = new GoodMoney(10)
  val money2 = money1.add(20)

  val sum1 = money1.add(20).add(10)
  println(s"Your sum1 is $sum1")
  val sum2 = money1.add(20).add(10)
  println(s"Your sum2 is $sum2")
}

What do you expect the output to be?
Of course it is the same
Your sum1 is GoodMoney(40)
Your sum2 is GoodMoney(40)

Hence, as you would observe that substitution when it is referentially transparent, saves us the headache of mapping state transitions, which is not easy. Immutability lends itself directly to referential transparency and substitution model. The evaluated expression when doing substitution is purely local and requires only local reasoning. If the operations on your object return another object and are immutable then you are good.

The code is present on the Knoldus GitHub Account. Till we meet again, write immutable code.

About Vikas Hazrati

Vikas is the Founding Partner @ Knoldus which is a group of software industry veterans who have joined hands to add value to the art of software development. Knoldus does niche Reactive and Big Data product development on Scala, Spark and Functional Java. Knoldus has a strong focus on software craftsmanship which ensures high-quality software development. It partners with the best in the industry like Lightbend (Scala Ecosystem), Databricks (Spark Ecosystem), Confluent (Kafka) and Datastax (Cassandra). To know more, send a mail to hello@knoldus.com or visit www.knoldus.com
This entry was posted in Scala and tagged , . Bookmark the permalink.

One Response to FunHop: Understanding Referential Transparency

  1. Pingback: FunHop: Working with Exceptions in Scala – Problem Statement | Knoldus

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s