
I have been a fan of functional programming for a while now. The reasons are plenty but mostly come from referential transparency. There is one feature of the FP approach that has been hard for me to explain to others. Due to the recent project I have finally started to have a better grasp on the subject and it comes down to using expressions instead of statements.
When writing Haskell, or other FP language code, expression approach is forced on the user but it can also be used in Java. Streams with lambdas are a great example of this.
What is the difference? Best to show on example:
Java Statements:
public String someMethodStatement() {
var usernameList = getUserNames();
var username = select(usernameList);
var modifiedUsername = doSomething(username);
// LOG :)
System.out.println(modifiedUsername);
return modifiedUsername;
}
Java Expressions (with minor modification, note: it can be written in multiple different ways):
public String someMethodExpression() {
return getUserNames().stream()
.filter(this::select)
.findFirst()
.map(this::doSomething)
.stream()
.peek(System.out::println)
.findAny()
.get();
}
For anyone that hasn’t been living under a rock in a Java community the second example should be totally understandable.
So why would I argue that the second example is potentially better code than the first one? One might say that it is actually less readable. Due to language limitations, it also changes from an Optional Monad back to a Stream in the middle of the execution. Those are valid concerns but miss one aspect: Scope.
In the third line:
var modifiedUsername = doSomething(username);
The operation has access to not only username variable but also to usernameList. Even though it is not used, when reading this, a programmer still has to keep a mental checklist of all the variables that are in the scope of the operation (just like a compiler 😉 ). Even if they are no longer needed. In the second example, when calling doSomething the code no longer has access to that list. The reader can focus only on the things that matter.
Since in Java this approach is somewhat clunky it might still be preferable to simply use statements. I will shamelessly plug that in Kotlin we can have this in a fluent and expressive form.
fun someMethodExpression() =
getUserNames()
.let(::select)
.let(::doSomething)
.apply(::println)
What we can see here are Scope functions. It almost looks like a mathematical equation and I love it. It has less code than the original Java Statement while still giving the benefit of an expression.
Scope is not the only advantage of using expressions. In this approach the code has a very clear start and SINGLE end of the function/operation. Having one exit from a function has been long said to be a good practice (unless you have performance reasons). Writing expressions forces this. No more multiple returns flying around a 200+ line method.
Last but not least, expressions guide us to better decompose our code. Instead of having one chunk of code after another, we have to separate them out into separate, clearly defined functions (or run a risk of deep indentations). This also helps to keep each function on one level of abstraction. Jumping between levels is harder when you do not have access to all the variables.
These are my reasons for preferring to write expressions over statements. They limit the cognitive load on the reader, encourage better practices and help keep the code modular.