Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

What Are Kotlin Progressions and Why Should You Care?

DZone's Guide to

What Are Kotlin Progressions and Why Should You Care?

When writing for-loops over ranges in Kotlin, Progressions can help you incorporate types that aren't supported by default. Here's a guide tailored for Java devs.

· Java Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Life Is Great

One of the first things that you notice when you start learning Kotlin is the "nice" for loop syntax that it has:

for (i in 1..5) { // mm, so fancy!
    println(i)
}


Cool, isn't it? An old, boring Java loop does not stand a chance.

for (int i = 0; i < 5; ++i) { // ugh, so boring!
    System.out.println(i);
}


As long as you stay in the world of trivial code samples or you iterate only over collections and ranges like the ones above, things are great. Kotlin is great, life is great, even politics don't bother you anymore.

Sad Times Arrive

Then, one day, you need to write a more sophisticated loop, say an equivalent of Java's:

LocalDate start = ...
LocalDate end = ...

for (LocalDate ld = start, !ld.isAfter(end); ld.plusDays(1)) {
    // stuff
}


"Ha, easy. I'll just switch to the Java-ish syntax and I'm fine", I thought in exactly such a case:

for (val ld = start; ld <= end; ld.plusDays(1)) {
    // stuff
}


An unpleasant surprise: There's no such construct in Kotlin! If you try to paste the snippet above to IntelliJ, it will automatically convert it to a while loop. Woops!

My next thought was: "Hey, there's this range syntax for dates that I used before. Maybe I can iterate over that..."

for (ld in start..end) {
    // stuff
}


Nope! That does not work either. At least not right away. As I learned a short while later, the secret of these nice Kotlin loops lies in the rangeTo operator and a bunch of base classes called Progressions.

RangeTo Operator

Whenever you type the double dot operator to create a nice range like "1..5" or "start..end", Kotlin translates the two dots in there into a call to the operator function called rangeTo.

As it turns out, Kotlin contains a few convenient implementations of the rangeTo operator for primitives and comparable types. The difference between the rangeTo for Int's and the rangeTo for LocalDate's is that the former returns an IntRange, while the latter returns a ClosedRange.

If you dig deeper into the implementation, you'll notice that IntRange extends a class called IntProgression, which in turn implements Iterable<Int>. That's the very reason why you can iterate over a range of Ints, while you cannot iterate over some other valid ranges of values.

Progressions

IntProgression is one of a few classes ending with the word Progression. The term denotes in Kotlin an Iterable that can use an arbitrary value as a step. You've probably seen already that in Kotlin you can do things like this:

for (i in 1..31 step 2) {
    println(i)
}


The step word here is nothing else than a call to an infix function called step, present in the IntProgression class. When you call this method, you get a new IntProgression with a step of requested size.

Our Very Own Progression

Now, equipped with all this valuable knowledge, we can finally solve our problem of iterating dates by implementing a progression ourselves!

class LocalDateProgression(override val start: LocalDate,
                           override val endInclusive: LocalDate,
                           val stepDays: Long = 1) : Iterable<LocalDate>, ClosedRange<LocalDate> {

    override fun iterator(): Iterator<LocalDate> = LocalDateProgressionIterator(start, endInclusive, stepDays)

    infix fun step(days: Long) = LocalDateProgression(start, endInclusive, days)
}


As you can see, there's no rocket science in there. Three parameters and trivial, one-line implementations and we have our very own, working progression. Obviously, we also need to implement the iterator that I've used in there:

internal class LocalDateProgressionIterator(start: LocalDate, val endInclusive: LocalDate, val stepDays: Long) : Iterator<LocalDate> {
    var current = start

    override fun hasNext() = current <= endInclusive

    override fun next(): LocalDate {
        val next = current
        current = current.plusDays(stepDays)
        return next
    }
}


And the last thing, we need to override the rangeTo operator for LocalDate, so that it uses our brand new progression:

operator fun LocalDate.rangeTo(other: LocalDate) = LocalDateProgression(this, other)


Tada!

for (ld in start..end step 2) { // mm, so fancy!
    println(ld)
}


Summary

As you can see, the for-loop over ranges in Kotlin is actually nothing magical. If your type is not supported by default, you can simply implement a new Progression, override the rangeTo operator, and your for-loops are fancy and shiny again. As for me, I'd actually prefer to be able to write a "normal" Java-ish for loop instead. "Ugh, so boring!"

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
kotlin ,java ,progressions ,for loops ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}