View all articles
Thinking functionally in Kotlin
August 18, 2021
Ali Hafizji

Functional programming is difficult to fathom for most application developers. Opinionated frameworks telling programmers how to build applications, reduces the desire to experiment. I often hear people saying the right way to build an application is "this". The very tinkering nature that drives change is left behind.

Mobile application developers fall in this bucket. The iOS and Android SDK is built in a certain way. Developers often think that's the only way to build applications. In this article, we're going to work with Kotlin. Kotlin can be used anywhere where Java is used. With the Android team adopting Kotlin many Android developers build applications using it. This has lead to a Java way of programming with Kotlin.

Functional Programming

Functional programming in a nutshell is applying arguments to functions. Functions are the basic building blocks of the program. Each taking arguments and returning a value. Functions are grouped together to build the program. There is no shared state as each function produces the same output given an input.

Android developers think of building applications using classes as building blocks. Classes maintain state and functions manipulate and use this state. This causes side-effects that lead to an unpredictable output. Add in threads and you've got a recipe for disaster.

An Android application needs to work within the boundaries of the system. This requires state. Kotlin supports both Object Oriented (OO) and Functional (FP) styles. Android developers should use this to their advantage. Parts of the Android application that don't involve the use of the system classes should be built in a functional manner. A well written functional program in Kotlin is modular, testable, and predictable.

Thinking functionally in Kotlin

Thinking functionally is hard. It requires the developer to break programs down into small functions. These functions are then composed to solve the problem at hand.

The best way to explain this is using the Naval Surface Weapons Center (NSWC) problem statement of 1993. The United States Advanced Research Projects Agency (ARPA) in collaboration with NSWC conducted this experiment. A problem statement was given and participants were asked to submit prototypes in different languages. This problem, a geometric region server was a component of a much larger system - AEGIS Weapon System (AWS).

The problem statement

A diagram explaining the problem statement to be solved in the article.

The problem statement is best described by the diagram above (think battleship).

  • Triangles: These represent friendly ships.
  • Min distance: This distance beyond which firing won't cause self-harm.
  • Firing Range: The range within which a target is within firing range.

The problem boils down to figuring out if a point is within firing range and isn't close to a friendly ship.

The Imperative Solution

  1. A data class called Point to store the x and y co-ordinates.
  2. A typealias for readability.
  3. A class that represents a battleship.
  4. A function that determines if a Position is within firing range.

Let's modify the inRange function to fulfil the conditions in the problem statement.

  1. targetDistance is the distance between the ship and the target.
  2. friendlyDistance is the distance between the friendly ship and the target.
  3. This condition checks if the target is within firing range and not close to a friendly ship.

Looking at the above code as more conditions are added the complexity increases. It's difficult to read, maintain, and test.

The Functional Solution

At the heart of it, we're finding if a Position is within firing range.

The above is a lambda that takes a position and returns a boolean. This lambda will be our basic building block.

Let's write a function that checks if a point is within range assuming the ship is at the origin.

This function takes the radius as an argument and returns a lambda. Given a point the lambda will return true/false if it is within the radius. This function assumes that the ship is always at the origin. To change this we can either modify this function or create another function that does the transformation.

This is known as a transformer function. It transforms the position by the offset and allows the caller to apply any inRange function to it. We could use the circle function defined before. This is one of the fundamental building blocks of functional programming. A ship at position 10, 10 with a circle radius of 20 will be described as:

We can define many more transformer functions. Here are a few:

Coming back to our original problem statement we can now construct the solution as show below:

The above code calculates the ship firing range and the friendly ship's min safety range. Then it finds the region that is the difference between the two and checks if the point in that region. This is a more declarative approach to writing the same solution. One that is build using functions and uses no state. You could argue that the first approach is easier to understand however when you start using these principles while building android apps the benefits of testing outweigh any argument.

Enjoyed this article? Don't miss out on more exclusive insights and real-life digital product stories at LeadReads. Read by Top C Execs.

Where to go from here?

In this article, we touched upon some functional programming concepts. We took a problem statement and build a solution in Kotlin.

It isn't possible to build an Android application using just functional programming. The application has to interact with different components of the system that do require state. However, pieces of the application that involve business logic can be built using these principles. This allows you to use composition, avoid side effects, and write code that is easy to test.

Note: The original solution to this problem statement was written in Haskell by Paul Hudak and Mark Jones.