3. Functional Programming

In this chapter we are going to look at some of the support that Kotlin offers for functional programming.

Content

1. What is Functional programming?

There are many definitions for this. Primarily, it’s a programming style, it’s a way of thinking about the code that involves things like immutability. In functional programming if you pass a value to a function, it will always return the same value. The data that you pass around is immutable and it doesn’t change, makes it easier to reason about what the application is doing.

Languages that support functional programming must treat functions as first-class citizens. In Java for example, objects are first-class citizens, we can pass objects around. Until Java 8, we couldn’t do that with functions. Since the idea of lambdas was introduced, we can now pass lambdas into functions as function parameters and we can return lambdas from functions as well.

This leads us to another aspect of functional programming – higher-order functions. These are functions that can create other functions, we can pass functions as parameters and return functions from a function.

How does all this fit in Kotlin? – We already know Kotlin supports immutability. We are familiar with the val keyword, if we declare a value as val that value is immutable, opposed to the var keyword which declares the value as mutable, it becomes a variable. We also have support for immutable collections – we can create versions of arrays, lists, etc. that will not change. As any language that supports functional programming, Kotlin treats functions as first-class citizens, it allows us to pass functions as parameters of other functions. There are also some functional constructs at our disposal, such as map function, flatMap function and there is a fold function.

2. Higher-order functions

Higher-order functions are functions passed to or returned from other functions. Here is an example, where we get a list of providers:

fun listAllProvider(providers: Providers,
fn: (String, String) -> Unit { // 1
fn(someValue)
}
listAllProviders(p) {
key, value -> println("Key $key : Value: $value") // 2
}

1 – Unit is the Kotlin understanding for nothing. There is no void type.
2 – the print method in pattern terms is a strategy. We get the data for these providers and we apply a strategy to it. The strategy in this case is print.

What if we want to use different strategy? If we are to use object oriented programming, we would pass an interface to a method, that interface would have a method like execute() and on each provider that we will get, we would call that execute() method and that method  would do something for each provider. In a functional language, rather than passing interfaces around, we can just pass functions around. Let’s refactor this piece of code:

fun main(args: Array<String>) {
getAllProviders()
}

fun getAllProviders() {
val allProviders = Providers.getProviders()
val it = allProviders.iterator()

while(it.hasNext()) {
val provider = it.next()
println(provider.name)

provider.forEach { key, value -> println("\t$key: $value")} // 1, 2
}
}

1 – forEach is a function that takes a parameter and that parameter is another function
2 – the forEach definition may not be obvious but it’s a function that takes two string parameters and returns nothing, it looks like this:

fun callback(val1: String, val2: String) : Unit

What we would like to do now, is to replace that hard-coded call to print line, with a call to some function that we provide. Or in other words, we will allow passing different strategies on the providers. The code would look like this:

fun main(args: Array<String>) {
getAllProviders { // 4
key, value -> println("\t$key: $value") // 5
}
}

fun getAllProviders(fn : (String, String) -> Unit) {  // 1, 2, 3
val allProviders = Providers.getProviders()
val it = allProviders.iterator()

while(it.hasNext()) {
val provider = it.next()
println(provider.name)

provider.forEach { key, value -> fn(key.toString, value.toString)} // 6
}
}

1 – first, we declare a variable fn that is going to be a function type, it’s not going to be an object, not an Int, nor a string but a function
2 – we need to replicate what we are passing the forEach – two String values
3 – using the goto operator, we specify what the function is going to return – a Unit (nothing)
4 – the braces are an indicator for the compiler that we are passing a function. Inside those braces we have to create a function that we are going to pass to getAllProviders call
5 – we don’t need to pass the types, we already know them – we need two strings. we just declare the variable names for those two strings and we pass them to the strategy we want to use – print
6 – finally, we pass the function that we passed as parameter with the two string representations of the values passed to the forEach

This is how high-order functions can be passed around in Kotlin. We could replace print with something else. We could sort or filter the data, for example. Essentially, what we are doing is using the strategy pattern, we are passing a strategy into a function to get it executed on our data.

3. Transforming data

Earlier, we mentioned Kotlin has support for functional constructs. Now let’s take a look at a few examples of how those can be used.

3.a map

Let’s start we the map. The idea is that we will pass it one thing and that thing will be mapped to something else. Let’s say we pass it a list of names and it maps it to a list of users. map operates on collections, turns a collection of one thing into a collection of other. map is used by passing the data we want to map and the thing we want to map it into:

data class ProviderDetails(val providerName: String, val name String)

class Providers {
fun getAllProviders() : List<ProviderDetails> {
val providers = Security.getProviders()
val listOfProviders = mutableListOf<ProviderDetails>() // 1

providers.forEach { provider -> val providerDetails = provider.entries.map {        entry -> ProviderDetails(provider.name, entry.key.toString()) //2 }

listOfProviders += providerDetails // 3
}
return listOfProviders
}
}

fun main(args: Array<String>) {
val providers = Providers()
val details = providers.getAllProviders()

details.forEach { detail -> println("${detail.providerName}, ${detail.name}") } }

1 – create mutable list in Kotlin
2 – Here, we are mapping all the entries that the provider has and what we are doing is, taking the given provider name and all of it’s entries and wrap all of it inside one single object by calling the ProviderDetails
3 – add an element to a list in Kotlin

When we are calling a higher-order function, we are passing an object, such as our entry and the detail from our example. In Kotlin, we don’t need to give names for these variables, Kotlin will provide a name for us – “it“. We just need to specify the behavior. So we can improve our example and make it look like this:

data class ProviderDetails(val providerName: String, val name String)

class Providers {
fun getAllProviders() : List<providerdetails> {
val providers = Security.getProviders()
val listOfProviders = mutableListOf<providerdetails>() // 1

providers.forEach {
provider -> val providerDetails = provider.entries.map {
ProviderDetails(provider.name, it.key.toString()) //2
}
listOfProviders += providerDetails // 3
}
return listOfProviders
}
}

fun main(args: Array<string>) {
  val providers = Providers()
  val details = providers.getAllProviders()

  details.forEach {
    println("${it.providerName}, ${it.name}")
  }
}

This makes the code a bit cleaner. Since we are at it, because ProviderDetails is a data class we can just do:

details.forEach {
println(it)
}

We can go even further. Because we are passing just a single parameter, we can do this:

detail.forEach(::println)

In this single line, Kotlin figures out by itself that we want to pass a function, this function takes a single parameter, that parameter is going to be called “it“, “it” is going to be of type ProviderDetails and Kotlin will call println on each of the things that we are passing. However, this is not the nicest things to do. If you are unfamiliar with this, it may trouble you to figure out what is happening.

3.b flatMap

Despite all the refactoring we have done so far, the code still “smells” a bit. If we look closer, we will notice that for each provider, we are adding the mapped list to the already existing list and then returning the full list. It feels like we should be able to return the data from the top level somehow, no? For good or bad, forEach returns nothing, however, map is very similar to forEach. It iterates over a collection and even return a collection. So what we can do is transform this:

fun getAllProviders() : List<providerdetails> {
val providers = Security.getProviders()
val listOfProviders = mutableListOf<providerdetails>()

providers.forEach {
provider -> val providerDetails = provider.entries.map {
ProviderDetails(provider.name, it.key.toString())
}
listOfProviders += providerDetails
}
return listOfProviders
}

Into this:

fun getAllProviders() : List<providerdetails> {
val providers = Security.getProviders()

return providers.map {
provider -> val providerDetails = provider.entries.map {
ProviderDetails(provider.name, it.key.toString())
}
}
}

What this would do is map two collections, the bad news is that it will return a List<List> and that’s not what we want, what we actually want is a List<ProviderDetails.

Even though map is great when working on a single collection, in cases where we use nested collections though we have to keep in mind how it behaves. Let’s say we have these two collections: A[1, 2, 3] and B[x, y]. If we call map on those, the result will look like this: [[1x, 1y], [2x, 2y], [3x, 3y]]. In order to merge the nested collections into one, instead of calling map, we can use flatMap. flatMap works in a similar fashion as map but it returns a single collection. Meaning, if we do flatMap on the same two collections, the result would be: [1x, 1y, 2x, 2y, 3x, 3y]. So using this knowledge, our final code would look like this:

fun getAllProviders() : List<ProviderDetails> {
  val providers = Security.getProviders()

  return providers.flatMap {
    provider -> val providerDetails = provider.entries.map {
      ProviderDetails(provider.name, it.key.toString())
    }
  }
}

4. Filtering data

Now, let’s say we don’t want all providers, instead we are looking for just a few that match our needs. Filtering data is one of the most common tasks software engineers come across. Kotlin provides us with a filter function which is again part of the collection library that takes a higher-order function. Let see how this works in code:

fun main(args: Array<string>) {
val providers = Providers()
val details = providers.getAllProviders("Random") //1

details.forEach { println("${it.providerName}, ${it.name}") }
}

fun getAllProviders(val filter: String) : List<ProviderDetails> { // 2
val providers = Security.getProviders()

return providers.flatMap {
provider ->  val providerDetails = provider.entries.filter{
it -> it.key.toString().contains(filter, true) // 3
}
.map { // 4
ProviderDetails(provider.name, it.key.toString()) }
}
}

1 – first, we add the criteria that we want to filter on to our get method. Let’s say we want to get all providers that generate random number, so we pass “Random” as parameter
2 – we make our get method accept a String parameter
3 – we check if the entry.key contains the filter, ignoring cases. This will filter out anything that doesn’t contain the filter string we are passing in
4 – on the return from filter we call map. Doing chains like this is completely acceptable and supported in Kotlin

With this we conclude this chapter. To sum up, we now know what is functional programming, we know what higher-order functions are and how to use them. We have seen how some of the functional constructs in Kotlin work. We were introduced to map, flatMap and filter and we understand how they work. Most of all, we have learned the benefits of functional programming and how much more efficient we can be in writing clean code.

 

<< 2. Object Oriented Programming                4. Kotlin and the Java Ecosystem >>