scalafunctional-programmingreferential-transparency

Exceptions and referential transparency


Reading "Functional Programming in Scala" and I'm a little confused by the section on exceptions not being referentially transparent.

The example given is

def failingFn(i: Int): Int = {
  val y: Int = throw new Exception("fail!")
  try {
    val x = 42 + 5
    x + y
  }
  catch { case e: Exception => 43 }
}

So the argument given in the book is that y is not referentially transparent because if we substitute it into the body in the try block we get a different result than if just run the function directly. This doesn't make any sense to me because the entire function is non-terminating to begin with so what is the point of saying values within the function body are not referentially transparent? The naive substitution in my mind would be as follows

def failingFn(i: Int): Int = {
  val y: Int = throw new Exception("fail!")
  try {
    val x = 42 + 5
    x + ((throw new Exception("fail!")): Int)
  }
  catch { case e: Exception => 43 }
}

and that still fails with the same exception.

Moreoever, y itself is a non-value (it can't directly be evaluated to a value) so what is the point of talking about referential transparency for such expressions? I suspect there is some kind of sleight of hand going on here so where exactly is my reasoning incorrect?


Solution

  • The point being made in the book is that, if it were truly referentially transparent, then you can remove the y variable entirely and replace it inside of the try/catch and you end up with different semantics.

    So, the point being made is that, in the case of exceptions, the point of evaluation for an exception matters.

    Maybe you can argue that the two programs are not semantically the same because of the location of the evaluation is what actually matters here. If you make the y lazy, then the outcomes do not change.