Kotlin’s standard library extends Java’s collections library to smooth out the rough edges. However, there are still some missing abstractions. This post will be a introduction on algebraic data types. We’ll cover two methods of constructing types. By the end we will see that all data types can be represented using products and sums and why sums are a useful addition to the standard library tool belt.

Types represent the possible values that can be produced by an expression. For example if an expression has a type of Int it represents numbers from -2147483648 to 2147483647. The number of possible values represented by Int is 2³². Int is finite. String on the other hand represents an infinite number of possible values. The number of possible String values can be represented using the infinite series, ∑ = c⁰ + c¹ + c² + … where c is the number of valid characters. Realistically String has some an upper limit because memory is not infinite.

Product Type

Product type is where two types are combined in a way that multiplies the number of possible values. For example, Pair<Int, Int> has 2³² × 2³² possible values. This should be familiar because every class declaration is a product of all its constituent member types. It is possible to construct all class declarations as products.

class Vector(val i: Int, val j: Int, val k: Int) // this is equivalent to typealias Vector = Pair<Int, Pair<Int, Int>>

Because these types share the same number of possible values, functions that map to and from the types are possible without losing information (https://en.wikipedia.org/wiki/Bijection). As an exercise, try to build a type that is isomorphic to Byte using only Pair and Boolean.

Sum Type

Sum type is where two types are combined in a way that adds the number of possible values. To encode this in Kotlin we use a sealed class.

sealed class Either<A, B> {

class Left<A, B>(val left: A) : Either<A, B>()

class Right<A, B>(val right: B) : Either<A, B>()

}

Either<A, B> is used to construct the possibilities of two distinct types. For example, Either<Int, Boolean> is an Int or a Boolean but not both. Either<Int, Boolean> has 2³² + 2 possible values. As an exercise, try to build a type that is isomorphic to Boolean using only Sum.

Error handling is a common use case for Either. For example an arithmetic operation may return a Int or an Exception.

// I've added this type alias and constructors because some

// readers felt the names left and right where ambiguous.

// This constrains the failure result to an exception.

// There may be times where failures have a different type for

// it's sad path like a String explaining the reason for form

// validation failing. typealias Result<V> = Either<Exception, V>

fun <V> Success(v: V): Result<V> = Either.Right(v)

fun <V> Failure(e: Exception): Result<V> = Either.Left(e) fun div(n: Int, d: Int): Result<Int> =

try {

Success(n / d)

} catch (e: Exception) {

Failure(e)

}

Since the possibility for an error occurring is part of the type, we ensure errors are handled by the caller. Using Either we can ensure our functions are truly functions and not subroutines.

Conclusion

Kotlin, and by extension Java, is missing a generic representation for sum types. We’ve seen how to build a generic sum type with Either<A, B>. Either can be used to encode errors in the return types of functions. If you like what you’ve read, please give a clap!