Kotlin: Don’t repeat the same mistakes Java made

Here are some of the reasons one might prefer Kotlin (and static types in general) over something like JavaScript:

  • IDE autocompletion
  • IDE refactoring
  • Finding errors sooner rather than later
  • Less need for unit tests
  • Self documenting code
  • Other IDE benefits like “jump to definition” and “show me the type of this var”

But here’s the thing:

It’s possible to write code in a statically typed language that loses all of these benefits.

1: External DSLs

Many years ago it became fashionable in Java frameworks to move large amounts of stuff out of java files and into xml files. As more and more stuff got off-loaded into xml we lost all of the things that type checking gave us.

This may be one of the reason many developers ditched Java in favor of dynamic languages like Ruby and Node. If you are not reaping the benefits of static types, why pay the cost?

2: Reflection

The same problems occur when framework developers get too fancy with their API’s, making heavy use of things like reflection. I recently came across an example of this in a Kotlin server-side web framework. Consider the following code snippet for accessing the content of an http post :

val map:ValuesMap = request.receive<ValuesMap>()

The problem is, there is no obvious way to determine what type parameter should be passed into the receive method (i.e. ValuesMap in this case) without searching through the source code. Here is what the IDE gives me:

Not much help, as you can see. I had no clue what type parameter (i.e. class) to pass into the receive method. It just says KClass. That could be anything.

I am used to this kind of thing in nodejs. That is, having to search through the docs and/or source code just to find out what parameters a function takes.

But I expect better things from Kotlin.

So after digging through the source code, I found the receive method which looked something like this:

fun <T : Any> receive(type: KClass<T>): T {
return when (type) {
ReadChannel::class -> getReadChannel()
InputStream::class -> getInputStream()
String::class -> contentAsString
ValuesMap::class -> computedValuesMap
MultiPartData::class -> getMultiPartData()
else -> throw UnknownContentAccessorRequest()
} as T
}

As you can see, they are using reflection to determine the type of the parameter I passed in (it’s a ValuesMap in my case).

Here is how I would have designed the API: Skip the refection and just use simple typed methods for the most common use-cases:

req.content.asParamMap(): ValuesMap
req.content.asString():String
req.content.asInputStream(): InputStream
req.content.asMultiPartData():MultiPartData

Moral of the Story

There are many alternatives to Kotlin. Many of them are dynamic languages like nodejs. If Kotlin is to compete with those platforms it needs to fully take advantage of it’s static typing.

Dave’s Advice For Framework Providers

Take advantage of static typing by keeping as much of the app as possible inside of Kotlin’s static type checking world:

  1. Avoid unnecessary external DSLs and config files.
  2. Don’t go overboard with reflection.
  3. Replace external DSLs with internal DSLs and APIs as much as possible. A great example of this is Kotlin’s HTML DSL.
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.