Immutability and the limitation of side effects in our programs work to reduce the complexity of those programs. Some code bases are complex out of necessity; the problem itself is complex. However, many code bases become complex because it's no longer clear how the state is expressed, managed, or manipulated. It is in cases like these where functional programming might be of great benefit.
Writing functional code in Kotlin can help us to reduce complexity in a several specific ways:
- Kotlin enables us to enforce immutability through the usage of data classes. Data classes make it extremely easy to define immutable models with which to represent state. The following is one such example in which we've defined an immutable data class to represent the current state of a UI:
data class ViewState(val title: String, val subtitle: String)
- Additionally, Kotlin provides a convenient syntax for creating a new state based on a previous one. This can be done using named arguments in conjunction with the copy constructor generated for any data class. Consider the following code:
val initialState = ViewState("Hello", "Kotlin")
val updatedState = initialState.copy(title = "Hey There!")
In this code, we're able to make a copy of the initialState variable while replacing the title property with the value "Hey There!". This allows us to explicitly update the desired title property without modifying any existing state.
- Because Kotlin supports top-level functions, we can reduce the complexity of our Kotlin code bases by avoiding the creation of utility and helper classes that serve no purpose other than to contain useful methods/functions. For example, in Java, we may have a class called DateHelpers, which is implemented as follows:
class DateHelpers {
private static final String pattern = "MM-dd-yyyy";
public String formatDateForUI(Date date) {
SimpleDateFormat simpleDateFormat = new
SimpleDateFormat(pattern);
return simpleDateFormat.format(date);
}
}
In Kotlin, however, we have top-level functions, and we can implement this same functionality without the need to explicitly create a class to contain a static method. A Kotlin implementation of this same functionality might look something like this:
private const val pattern = "MM-dd-yyyy"
fun formatDateForUI(date: Date): String {
val simpleDateFormat = SimpleDateFormat(pattern)
return simpleDateFormat.format(date)
}
These examples of enforcing immutability, the convenient and explicit copying of data classes, and the reduction in helper classes, can help reduce complexity within our code base. One of the biggest ways in which Kotlin supports functional programming and benefits from that support is through support for higher-order functions. In the next section, we'll explore how higher-order functions improve functional programming in Kotlin.