Functional Programming With Kotlin and Arrow — Algebraic Data Types

Learn how to use algebraic operations to better understand functional programming concepts like class constructs, typeclasses and lists in Kotlin & Arrow. By Massimo Carli.

Leave a rating/review
Download materials
Save for later
Share

Functional programming is magic, and in this tutorial, you’ll use simple algebraic operations to get a deeper understanding of the most important functional programming principles.

In the previous tutorial, Functional Programming with Kotlin and Arrow Part 4 – Generate Typeclasses With Arrow, you learned how to generate the code to implement important typeclasses like Functor, Applicative and Monad.

In this tutorial, you’ll build on that knowledge to learn:

  • What algebra is and how it translates to the class construct and the Either<E, T> typeclass in Kotlin.
  • How and when algebraic data types are useful, including a common practical example.
  • After addition and multiplication, you’ll see what the implications of exponents are.
  • What a simple List<T> has in common with algebra.

Understanding algebraic datatypes and how to use them will help you to master functional programming as they are very useful for encoding the business logic in applications.

Time to do some coding magic! :]

Note: The wonderful Bartosz Milewski’s Category Theory for Programmers course inspired this tutorial series. If you are new to Kotlin try Kotlin for Android: An Introduction to learn the fundamentals of Kotlin.

Getting Started

Download the materials for this tutorial using the Download Materials button at the top or bottom of this page. Open the project using IntelliJ 2019.x or greater and you’ll see the following structure:

Sample project structure

All the files are initially empty; it’ll be your job to add the code throughout this tutorial. But before writing any code, it’s important to understand the concept of algebra.

What Is Algebra?

Algebra is a generalization of arithmetic that lets you combine numbers and letters representing numbers by using specific rules. Here’s an example of a simple algebraic expression:

a * X ^ 2 - b * X + c

In this example, you have numbers like the 2, letters like a, b and c and operations like multiplication *, addition + and exponentiation ^.

Algebra is the set of rules that allow you to combine all those different symbols. But what does this have to do with Kotlin and functional programming?

Algebra and functional programming has a lot in common and programmers can use algebra to understand exactly how functional programming constructs work, starting with product types.

Data Types and Multiplication

The Kotlin APIs define many classes, including Pair<A, B>, which has the following simple code:

public data class Pair<out A, out B>(
    public val first: A,
    public val second: B
) : Serializable {
  // ...
}

This class consists of a simple pair of values, the first of type A and the second of type B.

In the first tutorial of the series, Functional Programming with Kotlin and Arrow: Getting Started, you saw that a type is a way to represent all the possible values that a variable of that type can assume. For instance, a variable of type Boolean can contain the value true or false.

What about the Pair<A, B> type? How many values are available for a variable of type Pair<A, B>? To understand this, consider the type you’re defining by copying the following code into Struct.kt:

typealias BoolPair = Pair<Boolean, Boolean> 

If you want to count all the possible values for a variable of type BoolPair, simply add the following code:

val bool1 = Pair(true, true)
val bool2 = Pair(true, false)
val bool3 = Pair(false, true)
val bool4 = Pair(false, false)

From a pair of Boolean variables, which you can consider as the value 2, you get 4 values in total. But do you get those four values by adding 2+2 or multiplying 2*2?

Answer this question by adding the following definition to the same file:

enum class Triage {
    RED, YELLOW, GREEN
}

This defines the Triage type, which is an enum with three different values: RED, YELLOW and GREEN. Next, add the following code:

typealias BoolTriage = Pair<Boolean, Triage>

This defines a Pair consisting of a Boolean and a Triage. Now, repeat the same question: How many values does this type have?

To find out, simply use the following code:

val triple1 = Pair(true, Triage.RED)
val triple2 = Pair(true, Triage.YELLOW)
val triple3 = Pair(true, Triage.GREEN)
val triple4 = Pair(false, Triage.RED)
val triple5 = Pair(false, Triage.YELLOW)
val triple6 = Pair(false, Triage.GREEN)

which proves that the possible values are:

Boolean * Triage = 2 * 3 = 6

This brings you to the conclusion that a Pair<A, B> has as many values as the product of multiplying the values of A by the values of B.

Now take a look at what happens when incorporating the Unit type into the multiplication.

Multiplying the Unit Type

In the first tutorial of this series, you learned that the Unit type has a single instance with the same name Unit. In Struct.kt, add the following definition:

typealias UnitTriage = Pair<Unit, Triage>

Now, note that the number of possible values is the value you get by adding the following code to the same file:

val unit1 = Pair(Unit, Triage.RED)
val unit2 = Pair(Unit, Triage.YELLOW)
val unit3 = Pair(Unit, Triage.GREEN)

You then have:

Unit * Triage = 1 * 3 = 3

This proves that the Unit is equivalent to the value 1 when you multiply with it.

Multiplying the Nothing Type

In the first tutorial of this series, you also learned about the Nothing type, which is a type with no values. What happens if you add the following definition to Struct.kt?

typealias NothingTriage = Pair<Nothing, Triage>

When you try to add the following code, you’ll get an error. This is because you can’t have a value of type Nothing so you can’t create an instance of the NothingTriage type.

val nothing1 : NothingTriage = Pair(???, Triage.RED)

This means that the type Nothing corresponds to the value 0 for multiplication purposes. In this case you can say that:

Nothing * Triage = 0 * 3 = 0

Multiplying Classes

In the previous examples, you used Pair<A, B> but what happens if you define a class, like so:

data class Struct(val enabled: Boolean, val triage: Triage, val value: Byte)

Based on what you learned in this paragraph, you can say that:

Struct = Boolean * Triage * Byte = 2 * 3 * 256 = 1536

The number of possible values is the product of multiplying all the values of the aggregated types. In this last example, Byte has 256 values, so the total number of values is 1,536.

But what happens when you do something like this instead:

data class Struct2(val enabled: Boolean, val triage: Triage, val name: String)

String has many possible values, so you can’t determine an exact result — but having an exact result is also not important. As you’ll see later, the important thing is to understand that you can represent the relationship between types as a multiplication operation.