Understanding Lambda Functions
Insight on some core concepts of lambda functions in programming.
As we learn programming, we are introduced to the concept of if-else
statements and how we can use them to write conditional code.
We are also taught about nested if-else
and else-if
ladder to write complex decision-making code. It’s all fun and games until we start writing complex code which requires multiple nested if-else
statements.
Let’s look at a simple problem to overcome by if statements.
The requirement of the problem is to process and validate a request then handle it. The diagram above shows the nesting of if of conditions to process the request.
public void processRequest(Request request) {
if (request != null) {
if (request.isValid()) {
if (request.isAuthorized()) {
if (request.isAllowed()) {
// Process the request
} else {
System.out.println("Request not allowed");
}
} else {
System.out.println("Request not authorized");
}
} else {
System.out.println("Request is invalid");
}
} else {
throw new IllegalArgumentException("Request cannot be null");
}
}
This code is a classic example of nested if-else statements. What’s wrong with this code?
if
and else
cases.Let’s try to refactor this code to make it more readable and maintainable.
The Return Early Pattern is a way of writing functions or methods so that the expected positive result is returned at the end of the function and the rest of the code terminates the execution (by returning or throwing an exception) when conditions are not met.
We implement this by negating the conditions and returning early with an appropriate clause statement if required.
if (something) {
doThis();
} else {
doThat();
}
if (!something) {
doThat();
return;
}
doThis();
This seems an extra effort for a simple if-else statement. But when we have multiple nested if-else statements, this pattern helps a lot.
Let us now try to refactor the above code using the Return Early Pattern.
public void processRequest(Request request) {
if (request == null) {
throw new IllegalArgumentException("Request cannot be null");
}
if (!request.isValid()) {
System.out.println("Request is invalid");
return;
}
if (!request.isAuthorized()) {
System.out.println("Request not authorized");
return;
}
if (!request.isAllowed()) {
System.out.println("Request not allowed");
return;
}
// Process the request
}
else
part of the nested if-else statements with a return
statement.We should always try to implement this pattern for our conditional programming. Testing and debugging such code is easier.
There are some key design patterns rule that are followed with this mindset:
A guard clause is a conditional statement that is used to check for conditions that would prevent the code from running correctly. Guard clauses are used to check for invalid input, and if the input is invalid, the code will return early.
This concept is the basis for the “return early” rule. While failing fast, the code is more robust because of the initial focus in finding the conditions where the code execution can terminate.
The Bouncer Pattern is a method to validate certain conditions by throwing an exception. For example, nullability checks and range checks.
public void doSomething(String id, Object value, int index) {
if (id == null) {
throw new IllegalArgumentException("Id cannot be null");
}
if (value == null) {
throw new IllegalArgumentException("Value cannot be null");
}
if (index < 0 || index > 10) {
throw new IllegalArgumentException("Index should be between 0 and 10");
}
// Do something
}
Following design patterns require extra effort and time to implement. These are some predefined convention which developers found over time to ease the workflow. However, we have keep them in the back of our head while approaching a problem.
Some developers might find it hard to read and understand the code written in this pattern. It’s a matter of personal preference. Let’s take a look at the following code:
public void useItem(Item item) {
if (!item.isValid()) {
return;
}
item.use();
}
public void useItem(Item item) {
if (item.isValid()) {
item.use();
}
}
Both the code snippets are doing the same thing. The first one is using the Return Early Pattern and the second one is the normal if-else statement. The second code snippet seems short and concise. This is a matter of personal preference.
We should always try to follow some key principles:
The Return Early Pattern is a good practice to follow when writing conditional code. It makes the code more readable, maintainable, and testable. It also helps in following some key design patterns like Guard Clause, Fail Fast, and Bouncer Pattern. However, this doesn’t mean we should apply it every time. We should always try to apply the best solution for the problem at hand.