referential transparencies
Oftentimes when shifting back and forth between languages it’s easy to fudge syntactic nuances out of habit. Sometimes when you fudge you learn something new. Today I fudged some Java in my Scala.
What would you guess the return type of the following method to be?
def guess(what: String) {
"%s? really?" format what
}
One who is used to Scala by now knows there’s no need to explicitly declare a return type, let alone use the return keyword at the end of a method thanks to this fancy pants thing called type inference.
So what’s the fuss with this simple example? It obviously returns a String, duh. WRONG.
Scala is part functional and part of that functional nature includes referential transparency.
Functional Projectors
Referential what? Databases? Overhead projectors?
So what does referential transparency mean? It’s simple.
Say you have a value v and are assigning it the result of function f which takes the argument a:
val v = f(a)
Function f is said to be referentially transparent, if you can replace all occurrences f with the the result of applying the same value a to f and always end up with the same result.
Huh?
In functional programming, the idea is that functions should be side effect free, so it’s okay to replace f(a) with just the resulting value of f(a) because your program will not modify state that would adversely affect the result of f(a).
Mixed Flavors
So what’s this have to do with fudge and Java?
As Scala has inherited some of Java’s syntax, Javaisms can creep into your Scalaisms.
Case in point:
def guess(what: String) {
"%s? really?" format what
}
The Scala return type of this method is actually Unit. The correct way to declare this method in Scala is:
def guess(what: String) = {
"%s? really?" format what
}
or even better (more minimal)
def guess(what: String) = "%s? really?" format what
So what changed? You assign the name of the function to the it’s functional body. If you’ve been paying attention, you’ll pick up on the fact that this is the functional style. If you are being referentially transparent the about your design you can say
val stringGuessResult = "String? really?"
val guessResult = guess("String")
stringGuessResult == guessResult
where the resulting value is always the same.
In the same manner declaration of function names follow suit.
fnNameWithValue = value
fnNameWithFunctionBody = functionBody
fnNameWithValue == fnNameWithFunctionBody
Without the assignment operator, the { ... } brackets from the previous example merely serve as a means of scoping local variables. The environment surrounding the brackets can not know what kind of scheme the local variables are plotting.
Border Crossing
This key-value concept of assigning function names to function bodies is also common in other languages. Let’s take the same example in Javascript.
As Javascript also inherited some of its syntax from C, you can declare methods in a C-like fashion
function guess(what) {
what + "? really?"
}
You can also write this in the functional style
var guess = function(what) {
what + "? really?"
}
Does this look familiar?
var someModule = (function(){
return {
guess: function(what) {
what + "? really?"
},
tell: function(story) {
"my story is "+ story
}
};
})();
Or even
$.ajax({
url: "...",
context: document.body,
success: function() {
alert("All done!");
}
});
Both are use cases for using the functional style of assigning a function body to a name.
Back to Our Story
Just for fun let’s say you actually did assign and explicit return type for the previous example with out assigning the the method body.
def guess(what: String): String {
"%s? really?" format what
}
You actually end up with a compile error.
illegal start of declaration (possible cause: missing `=' in front of current method body)
Ops! You’re Java’s showing.
Overlapping Transparencies
When you think about it, referentially transparency seems the like same idea behind vals. You are assigning some resulting value to a name which cannot change unless explicitly overridden in a sub type. For a program to be side effect free, one might say that when part of a program accesses a val, the val should always return the same referentially transparent result.