Advent of Kotlin


Since 2018 I started participating in Advent of Code with the goal of learning new programming languages. This year I chose kotlin, and the experience was very good. I’ll list some of the language features I liked so far.

Sealed Classes

For Day’s 14 solution I wanted to have a class Entry and different subclasses of it. The only difference with normal OOP inheritance is that I wanted to restric all the object to belong exactly to a single subclass. It turns out that sealed classes are designed for this behavior.

sealed class Entry
data class Mask(val mask: CharArray) : Entry()
data class Mem(val addr: Long, val value: Long) : Entry()

When statement

Now, in order to evaluate the different kinds of Entry we need to conditionally check its “real” type. This is a good use case for the when statement:

when (entry) {
  is Mask -> mask = entry.mask
  is Mem -> memory[entry.addr] = applyMask(entry.value, mask)
}

Since the class was sealed, the compiler knows all the possible subclasses and will verify that there is a proper evaluation for all of them. For instance, if we add a new subclass of Entry we will get an error in the code above (which would be uncaught otherwise).

If we add the subclass Extra, the compiler will show the following error: 'when' expression must be exhaustive, add necessary 'is Extra' branch or 'else' branch instead.

Another nice property of the when statement is that we can use it for conditional branching executions (an statement), but also as an expression.

  • Conditional execution, this is very similar to switch in other programming languages (nothing surprising here):
    val a = 10
    when (a) {
        3 -> println("first condition")
        4 -> println("second condition")
        else -> println("else")
    }
  • Statement: This allows to reduce unnecessary mutability in the message, unlike the if alternative:
    // Immutable (defined as val)
    val msg = when(a) {
        3 -> "first condition"
        4 -> "second condition"
        else -> "else"
    }
    // Mutable (defined as var)
    var msg = "else"
    if (a == 3) {
        msg = "first condition" 
    } else if (a == 4) {
        msg = "second condition"
    }

Note: Initially I thouhgt that having val and var was very confusing, but in practice I found it handy and easy to remember ¯\(ツ)

Smart Cast

Kotlin keeps track of the results of the is operator, which becames really handy in the case you need to downcast an object. Based of the code above, let’s check the differences between Java and Kotlin.

if (expr instanceof Mask) {
    // cast needed in order to access "Mask" members.
    System.out.println(((Mask)expr).mask);
}
if (expr is Mask) {
   // No cast needed.
   print(expr.mask)
}

Functional flavor

After a few solutions I got used to the functional-like approach that kotlin uses, the support for lambda functions is really good and the code ends being clear and concise. This will also help to reduce mutablility in some places.

Example, let’s suppose we have a file 01.in with several lines representing integer numbers, and we want to compute the sum of all of them.

// Both variables can be immutable.
val numbers = File("data/01.in").readLines().map { it.toInt() }
val sum = numbers.sum()
// Do something else with numbers
// Both variables must be mutable.
var numbers = mutableListOf()
for (line in File("data/01.in").readLines()) {
    numbers.add(line.toInt())
}
var sum = 0
for (n in numbers) {
    sum += n
}
// Do something else with numbers

Data Classes

In most of the challenges I ended creating classes that only hold information, and use those classes in maps or sets (which require some sort of comparator or hash). I also often want to print these objects.

In C++ I would end doing something like:

struct Entry {
    int add;
    long value;
    // Default comparator since C++ 20: https://en.cppreference.com/w/cpp/language/default_comparisons
    auto operator<=>(const Entry&) const = default;
}

ostream& operator<<(ostream& os, const Entry& entry) {
    os << '{' << entry.add << ": " << entry.value << '}';
    return os;
}

But in kotlin, it is a one-liner :D.

data class Entry(val add: Int, val value: Long)

Next ?

I will keep trying Kotlin in other scenarios, and update this post if I found somethig interesting. Also, if you have interesting ideas for next year’s advent of code, please let me know! (contact in the “about” page of this site).