Kotlin — The Missing Parts

Jerzy Chałupski
7 min readAug 30, 2018

--

As you might have judged from my previous post, I’m a big fan and a proponent of the Kotlin programming language. While the language in its current state is, in my opinion, a vast improvement over Java, it could have been even better. After using it in my day job for several months, I have accumulated a list of features I’d like to see added to Kotlin.

SAM conversion for Kotlin interfaces

I’ll start with my personal pet peeve: the fact that Kotlin interfaces are second-class citizens when compared to Java ones.

// in Java file
interface JavaInterface {
void doJavaThings();
}
// in Kotlin file
interface KotlinInterface {
fun doKotlinThings()
}
// usage
val javaInterface = JavaInterface { println("Java!") }
val kotlinInterface = object : KotlinInterface {
override fun doKotlinThings() {
println("Kotlin!")
}
}

It seems I’m not the only one who doesn’t like this difference. This feature was the 2nd most voted one in the last year’s survey and the corresponding YouTrack issue has over 100 votes.

You may argue that one can use the Kotlin’s function types instead of SAM classes or functional interfaces, but that’s a mistake in my opinion. Not all (String) -> String functions are interchangeable: one migth convert the String to uppercase, another one migth return a hex representation of some hashing function. Using a single type to represent both actions means losing some information about the use case and opening a possibility of mixing up the actions: passing the uppercase function where the hashing function is expected.

If you want to mention typealias HashingFunction = (String) -> String as a solution, here’s my take on this.

Intermission: a typealias rant

I think that adding the typealias feature was a mistake. It’s nice syntactic sugar to reduce the text duplication and give a meaningful name to some constructs:

typealias PendingResponse = Single<Option<Response>>

On the flipside, it encourages a lot of questionable practices. It can give you the false sense of type-safety:

typealias Username = Stringfun greetUser(username: Username) { … }fun uhOh(someRandomString: String) {
greetUser(someRandomString) // no error or warning here!
}

It encourages the “primitives obsession”, i.e. writing stuff liketypealias UsersByShoeSize = Map<Int, List<User>> instead of using proper encapsulation:

data class UsersByShoeSize(private val data: Map<Int, List<User>>) {
operator fun get(shoeSize: Int) = data.getOrElse(shoeSize) { emptyList() }
}

Note that the data class above does not expose all other methods of underlying Map and it encapsulates a sane behavior for all keys.

This use case is especially common for aliasing function types because there’s no SAM conversion for Kotlin classes.

typealias MyValidator = (String) -> ValidationStatus

Yes, you can pass the lambda to create a MyValidator, but as I mentioned earlier, you should have been able to do that with a regular interface as well!

I’d love to hear your take on this feature — am I missing something here?

Zero-cost wrappers over other classes

Probably the most abused type in your codebase is a poor String: it represents the usernames, email addresses, and identifiers of several completely unrelated entities.

The alternative is to create a data class with a single String property for each use case, but there are two issues with that approach:

  • The usage is a bit clunky, as you have to access actual value through the property. So you have val email = Email(…), but later on, you have to write email.value or something similar.
  • Wrapping the underlying data and accessing it via property instead of using it directly incurs an overhead. It might be negligible, but it looks like an unnecessary waste.

The feature is included in an upcoming 1.3 release under the name “Inline classes” and while it doesn’t address the first point (you still have to access the wrapped data through property syntax) I think it’s going to be incredibly useful and I can’t wait to get my hands on it.

Self-type

Let’s jump to some more exotic feature request. Imagine you have the following class:

open class Calculator {
protected var ans: Double = 0.0
fun add(value: Double) = apply { ans += value }
// bunch of other operators
fun result() = ans
}

And then you want to extend it with more complex operators:

class ScientificCalculator : Calculator() {
fun sin() = apply { ans = kotlin.math.sin(ans) }
}

If you try to use it, you’ll run into unpleasant surprise:

val result = ScientificCalculator()
.add(PI)
.sin() // error!
.result()

The problem here is that the add method is defined on the base Calculator, which means that after calling it we lose the information that our instance of Calculator is really the ScientificCalculator, which we can use to calculate the sine.

One workaround you can apply is a self-referencing generic, but this pattern is rather complex and I’d rather not expose it in the public API.

What we really need is a way to tell the compiler that the method always returns the type of current instance, whatever that type is:

fun add(): this = apply { … }

Unfortunately, as far as I know, there are no plans currently to add this feature, and one of the Kotlin developers mentioned that adding it might be very difficult.

Union types

The difference between proper union type and Kotlin’s sealed class is the way you define the members of the type. In case of sealed class this information is a part of the members’ definition:

sealed class UserIdentifier
data class Username(/* … */) : UserIdentifier()
data class Email(/* … */) : UserIdentifier()

This obviously has some limitations:

  • You can’t add the 3rd party classes to your sealed class without some wrapper class. In the example above even if the Username is simply a String, you have to wrap it in another class.
  • A single class can belong only to a single sealed class hierarchy. In the example above the Email is used as a way to identify the user, but another use case for this data is providing a contact information for the user. Since there’s no way to use the same data class as a member of another sealed class, you’d probably end up with a carbon copy of this class in ContactInfo sealed class and convert them back and forth.

A proper union type would bypass both limitations by moving the membership info from members’ definition to the union type itself:

data class Username(/* … */)
data class Email(/* … */)
data class PhoneNumber(/* … */)
uniontype UserIdentifier = Username | Email
uniontype ContactInfo = Email | PhoneNumber

Pattern matching

A while ago I read the book “Purely Functional Data Structures” and one thing that stood out was the brevity of implementation of even quite complex algorithms. The author used Standard ML for all code samples and I think that one of the reasons behind the terseness of code is the pattern matching feature of that language.

The best example of concise, but very clear code from this book is a balance method of a Red-Black tree. Quick refresher: a red-black tree is a self-balancing binary tree where each node is “colored” either black or red. If we ensure that no red node has a red child and all paths from the root to leaf nodes contain the same number of black nodes, we get a better worst-case scenario performance.

In this particular example we have a data structure like this:

sealed class RedBlackTree<E : Comparable<E>>
object Empty : RedBlackTree<Nothing>()
data class Tree<E : Comparable<E>>(
val color: Color,
val left: RedBlackTree<E>,
val element: E,
val right: RedBlackTree<E>
) : RedBlackTree<E>()

And we want to write a fun balance(): Tree<…> method of a Tree class that fixes the paths with two subsequent red nodes. This method is recursively called for each insert operation, so all we need to do is to detect when a child node is red and one of its children is red as well.

Kotlin implementation quickly becomes a bunch of nested if statements and is Tree checks that are easy to mess up and very hard to read later on. Hypothetical syntax for pattern matching in Kotlin would turn that mess into this:

fun balance(): Tree<E> = match (this) {
Tree(B, Tree(R, Tree(R, a, x, b), y, c), z, d),
Tree(B, Tree(R, a, x, Tree(R, b, y, c)), z, d),
Tree(B, a, x, Tree(R, Tree(R, b, y, c), z, d)),
Tree(B, a, x, Tree(R, b, y, Tree(R, c, z, d))) -> Tree(R, Tree(B, a, x, b), y, Tree(B, c, z, d))
else -> this
}

I guess the effect won’t be as extreme in most cases, but I think this feature would be a great addition to Kotlin.

Check out this article for a preliminary discussion of pattern matching feature for Java language for more info and examples.

Ternary operator

And finally, I have to admit that I’m one of those folks who spent too much time staring at Java and C code and I dearly miss the good ol’ ternary operator. I simply find the following code:

val result = someCondition.isMet()
? trueValue
: falseValue

More readable than this (regardless of the formatting; trust me, I’ve tried):

val result = if (someCondition.isMet()) trueValue 
else falseValue

What’s on your wishlist?

I hope that at least some of the features I listed above will make their way into the language. In the meantime, I’m curious what’s on your list. Leave the response or ping me on Twitter!

--

--