As part of my work at Certora, Inc. I’ve had the displeasure to become acquianted with Kotlin. My goodness what a crap language and crap ecosystem. It’s even more shocking that this is the language we expect every Android app to be developed in. God help us.

The “Ecosystem”

I suspect like many people, I use JetBrains’ Intellij IDEA for Kotlin development. It is a garbage experience. I appreciate that IDE development is probably quite difficult, but the problems listed here are even more embarrassing when you consider that the folks who develop Kotlin and the folks that make IDEA are the same goddamn team!

  1. IDEA doesn’t seem to understand Kotlin: I cannot count the times that IDEA has informed me that some cast or type arguments aren’t actually necessary, and even helpfully offer to remove the unnecessary component. Once I do, IDEA immediately freaks out and demands I add clarifying cast/type arguments/etc.
  2. Related to the above: the incremental compiler used within the IDE disagrees with the compiler used by the Maven plugin! Code that IDEA thinks is just fine, the Maven compiler complains about (usually RE: ambiguous type arguments).
  3. There are hilarious “auto-completion” bugs. Very frequently I will have a data class that doesn’t yet implement some methods required by it’s type.

    IDEA, like any good IDE, offers to add the missing implementations:

    Let’s see how it does:

    What on earth has happened here? IDEA has placed the one of the stub implementation in the surrounding scope. The other requested change has been dropped, completely.

  4. Other fun bugs: IDEA will frequently throw exceptions when asked to do basic tasks during the course of regular operation. That little red icon is constantly flashing in the background.
  5. Complete, catastrophic failure: this is also related to the garbage compiler. Try putting this snippet of Kotlin somewhere in your source file "${foo.bar.try}". This is obviously a syntax error: try is not a valid field name. This snippet completely crashes the Kotlin compiler. I do not mean the compiler gives an error, I mean the compiler dies with an absurd stack trace:

    The story for IDEA is even worse:

    It just sits spinning that loading icon, refusing to even load a basic text editor. In the background, the compiler invoked by IDEA has encountered the same catastrophic failure which has silently crashed the “open text editor” action. The only indication you have that something is even wrong is the tiny blinking red icon in bottom left corner or if you look in the log.

    Of course, to fix this error, you need to go find yourself a real text editor, just so IDEA can open a file.

    Pathetic.

The Language Itself

For a language that purports to be more expressive and usable than Java, it is weirdly hamstrung by several sharp edges. Some of these are admittedly corner cases, but others are glaring deficiencies.

  1. Subclasses of a sealed class do not have to be nested within the body of the sealed class, but only if the sealed class itself is not nested within anything. Why?
  2. Terrible errors: very often I will write code that trigger a “Type-checking has encountered a recursive problem”. What? What a weird thing to report for Joe and Jane Programmer.
  3. No polymorphic constructors: I recognize this is a weird corner case to complain about, but I’ve come across legitimate cases where I need a constructor polymorphic in some types when the class itself is not generic. This is simply impossible in Kotlin, there is not even a plausible syntax that could be used.
  4. Unnecessary verbosity:
    1. No ternary operator. If you want an in-place conditional, get ready to write out if(c) { t } else { f }. So concise!
    2. Want a shared constant used by all your classes? Easy! Just write:

         companion object {
           val my_constant = /* ... */
         }
      

      Way shorter than public static final ....

      (And no, you can’t used const in the definition of my_constant)

    3. Complicated pattern matching becomes a nightmare. What to check that a value is a data class whose field is another data class? Easy:

         when(op) {
            op is MyDataClass -> when(op.field) {
              is SomeOtherDataClass -> /* ... */
            }
         }
      

      You could also get it on one line with:

        when {
          op is MyDataClass && op.field is SomeOtherDataClass ->
        }
      

      … but you lose exhaustivity checking.

      Equivalent OCaml:

       match v with
       | MyDataClass(SomeOtherDataClass x, _) -> ...
      
  5. Garbage type inference. I recognize that whole program type inference may be challenging to implement. But you can do better than giving up on:

      fun mapIt(): Map<Int, Int> {
        val x = mapOf()
        return x
      }
    

    There may be some argument here about predictability or performance, but frankly, it’s pretty unpredictable now when type inference will work, and when it won’t.

  6. No local typealias: This is awful. Do you have a type you use frequently within a class that is a complicated generic type? Would you love to be able to write typealias Worklist = List<Pair<String, WorkItem<Int>>> without creating top level typealiases?

    Tough! You can’t!

    As best as I can tell the stated reason from the Kotlin developers is: “If the typealias contains type variables, you get dependent types.” Okay sure. It’s super easy to check if a typealias exposes a type variable; the OCaml type checker has a check to ensure locally abstract types do not escape their scope.

  7. No package visibility. I recognize that there is an internal visibility which is close, but only restricts to modules. The stated reason from the Kotlin developers is roughly: “But anyone could just declare their class as in your package, and get access to this type/class/state/whatever”. That is certainly true, but it’s also true of protected visibility which the Kotlin developers have no problem including.

    So either pollute your namespaces, or make tons of modules I guess.