Auto Added by WPeMatico

Idiomatic Kotlin: Solving Advent of Code Puzzles, Day 2

Let’s continue learning how to write idiomatic Kotlin code by solving the Advent of Code tasks! Today, we’re discussing the solution for the day 2 task.

Day 2. Password philosophy

We need to confirm that passwords meet the corporate policy. Find the full task description at https://adventofcode.com/2020/day/2*.

First, we need to read the input:

1-3 a: abcde
1-3 b: cdefg
2-9 c: ccccccccc

Each line contains the password policy and the password. Our task is to check that the password is valid and conforms to the given policy. The policies are different in the first and the second parts of the task.

In the first part, the password policy indicates the lowest and highest number of times a given letter must appear for the password to be valid. For example,

1-3 a

means that the password must contain

a

at least once and at most 3 times. In the example, two passwords, the first and the third ones, are valid. The first contains one

a

, and the third contains nine

c

s, both within the limits of their respective policies. The second password,

cdefg

, is invalid, as it contains no instances of

b

but needs at least 1.

In the second part, the policy describes two positions in the password, where 1 means the first character, 2 means the second character, and so on (indexing starts at 1, not 0). Exactly one of these positions must contain the given letter. Other occurrences of the letter are irrelevant. Given the same example list from above:

  1. 1-3 a: abcde

    is valid: position 1 contains

    a

    and position 3 does not.

  2. 1-3 b: cdefg

    is invalid: neither position 1 nor position 3 contains

    b

    .

  3. 2-9 c: ccccccccc

    is invalid: both positions 2 and position 9 contain

    c

    .

We should count the number of passwords from the given input that are valid according to the interpretations of the policies.

As usual, if you haven’t done it, please solve the task yourself first. Here’s how you can set up Kotlin for this purpose.

Solution

First, we should read and parse the input. Let’s create a

data

class for storing a given password together with its policy:

data class PasswordWithPolicy(
   val password: String,
   val range: IntRange,
   val letter: Char
)

Each policy specifies an integer range and a letter that we store in the

IntRange

and

Char

types, accordingly. The modifier

data

instructs the compiler to generate some useful methods for this class, including the constructor,

equals

,

hashCode

, and

toString

.

Parsing input

Let’s write a function that parses one input line into this class. Since it’s a function that creates an instance, let’s put it into a companion object in our data class. Then we can call it by the class name:

PasswordWithPolicy.parse()

.

data class PasswordWithPolicy(...) {
   companion object {
       fun parse(line: String): PasswordWithPolicy { 
           TODO()
       }
   }
}

While developing, you can put the

TODO()

call that throws the

NotImplementedError

exception. Because it returns the

Nothing

type, which is a subtype of any other type, the compiler won’t complain. The function is supposed to return

PasswordWithPolicy

, but throwing an exception inside is totally valid from the compiler’s point of view.

There are two ways to parse the input: by using utility functions on Strings or by using regular expressions. Let’s discuss both of these ways.

The Kotlin standard library contains many useful functions on

String

s, including

substringAfter()

and

substringBefore()

which perfectly solve the task in our case:

// 1-3 a: abcde
fun parse(line: String) = PasswordWithPolicy(
   password = line.substringAfter(": "),
   letter = line.substringAfter(" ").substringBefore(":").single(),
   range = line.substringBefore(" ").let {
       val (start, end) = it.split("-")
       start.toInt()..end.toInt()
   },
)

We provide the parameter names explicitly while calling the

PasswordWithPolicy

constructor. The input format convention requires that:

  • password

    be the string that goes right after the colon

  • the letter go between the whitespace and the colon, and
  • the range go before the whitespace and consist of two numbers split by “
    -

To build a range, we first take the substring before the whitespace and split it with the “

-

” delimiter. We then use only the first two parts (

start

and

end

), and build a range using the “

..

” operator converting both lines to

Int

s. We use

let

to convert the result of

substringBefore()

to the desired range.

In this code, we assume that all the input lines follow the rules. In a real-world scenario, we should check that the input is correct.

substringAfter()

and similar functions take a second argument default to the string itself that should be returned in the event the given delimiter is not found.

toInt()

and

single()

have

toIntOrNull()

and

singleOrNull()

counterparts returning

null

if the string can’t be converted to an integer number or consists of more than one character.

Let’s now implement the same logic using regular expressions. Here is a regular expression describing the input format:

(d+)-(d+) ([a-z]): ([a-z]+)

First are two numbers split by -, then a letter and a password.

Our new

parse

function matches the line against a regular expression and then builds

PasswordWithPolicy

on the result:

private val regex = Regex("""(d+)-(d+) ([a-z]): ([a-z]+)""")
fun parseUsingRegex(line: String): PasswordWithPolicy =
   regex.matchEntire(line)!!
       .destructured
       .let { (start, end, letter, password) ->
           PasswordWithPolicy(password, start.toInt()..end.toInt(), letter.single())
       }

In Kotlin, you use the

Regex

class to define a regular expression. Note how we put it inside a triple-quoted string, so you don’t need to escape

 

. For regular strings, escaping is done in the conventional way, with a backslash, so if you need a backslash character in the string, you repeat it:

"(\d+)-(\d+)".

We assume that our input is correct, which is why we use

!!

to throw an NPE if the input doesn’t correspond to the regular expression. An alternative is to use the safe access

?.

here and return

null

as the result.

The

destructured

property provides components for a destructuring assignment for groups defined in the regular expression. We use its result together with

let

and destruct it inside the lambda expression, defining

start

,

end

,

letter

, and

password

as parameters. As before, we need to convert strings to

Int

s and

Char

.

Password validation

After we read the input, we need to validate whether the passwords comply with the given policy.

In the first part, we need to ensure that the number of occurrences of a given letter is in a range. It’s one line in Kotlin:

data class PasswordWithPolicy(
   val password: String,
   val range: IntRange,
   val letter: Char
) {
   fun validatePartOne() =
       password.count { it == letter } in range
   ...
}

We count the occurrences of

letter

in the

password

string, and check that the result is in

range

.

in

checks that the given element belongs to a range. For numbers and other comparable elements,

x in range

is the same as writing explicitly

range.first <= x && x <= range.last

.

The second part isn’t difficult. We need to check that exactly one of the positions (stored in the range) contains the given letter. We use the boolean

xor

operator for that, which returns

true

if the operands are different. That’s exactly what we need here:

fun validatePartTwo() =
   (password[range.first - 1] == letter) xor (password[range.last - 1] == letter)

We need to subtract 1 from

first

and

last

because they imply indexing from one, but string indexation starts from zero.

Added all together, this gives us our

main

function that finds the result for both parts:

fun main() {
   val passwords = File("src/day2/input.txt")
       .readLines()
       .map(PasswordWithPolicy::parse)
   println(passwords.count { it.validatePartOne() })
   println(passwords.count { it.validatePartTwo() })
}

Note how we pass a member reference

PasswordWithPolicy::parse

to the

map

function to convert the strings to passwords.

That’s it! We discussed how to solve this problem, and along the way, looked at string utility functions, regular expressions, operations on collections, and how

let

helps transform the expression nicely to the form you need.

Please let us know if you find this format useful and would like us to provide solutions for more difficult tasks!

*Used with the permission of Advent of Code (Eric Wastl).

Continue ReadingIdiomatic Kotlin: Solving Advent of Code Puzzles, Day 2

4 Articles & some news from Kt. Academy ⭐️

Hello 👋

Today we’re coming to you with many great articles and some exciting news! 🤩

Find a quick shortcut below, but don’t forget to scroll further!

⇢ Articles:
👉 Consider extracting non-essential parts of your API into extensions
👉 Avoid member extensions
👉 Kotlin coroutines dispatchers
👉 Kotlin DSL, BuildSrc, Product Flavors, Flavor Icon and more

Become our trainer!
Functional Kotlin workshop

Enjoy!

What are the differences between members and extensions and in what cases we should prefer one over another 👇
Consider extracting non-essential parts of your API into extensions written by Marcin Moskała.

Effective Kotlin Item 45: Consider extracting non-essential parts of your API into extensions

What are member extensions❓ How are they possible❓
And… why should we avoid using them❓

Find the answers to all these questions in the article written by Marcin Moskala
👉 Avoid member extensions

Effective Kotlin Item 46: Avoid member extensions

An important functionality that Kotlin Coroutines library offers is letting us decide on what thread a coroutine should be running. This is done using dispatchers.

To learn where we should use each dispatcher from the Kotlin coroutines library read the latest article written by Marcin Moskala 👇

Kotlin coroutines dispatchers

Do you want to know how to migrate Gradle files to Kotlin DSL, and implement buildSrc?
Check this article Kotlin DSL, BuildSrc, Product Flavors, Flavor Icon and more written by Mustafa Yiğit.

Kotlin DSL, BuildSrc, Product Flavors, Flavor Icon and more

Kt. Academy connects world-class specialists with companies of all sizes ✨

Do you want to become one of our trainers and share your knowledge with those in need?

Fill this form and join our programming family 🔥

Become our trainer

❗️Leverage your skills and improve your productivity with functional programming features in Kotlin❗️

Functional Kotlin with Kt. Academy 🔥
👉 1 day
👉 a lot of practice
👉 for experienced Kotlin developers

Check more details and register here ⤵️

Functional Kotlin

That’s all for today!

Thanks and see you soon!

Kt. Academy Team

www: kt.academy
Blog: blog.kotlin-academy.com
Twitter EN: @KtDotAcademy
Twitter PL: @ktdotacademyPL
FB: @KtDotAcademy
LinkedIn: @Kt. Academy


4 Articles & some news from Kt. Academy ⭐️ was originally published in Kt. Academy on Medium, where people are continuing the conversation by highlighting and responding to this story.

Continue Reading4 Articles & some news from Kt. Academy ⭐️

Solving Advent of Code Puzzles in Idiomatic Kotlin

What’s the best way to learn a language other than writing some code with it? Solving fun and short tasks like the ones from Advent of Code might be a great opportunity to practice your language skills, and you can learn a lot if you compare your solutions with how others have solved the same problem.

Lots of developers from around the world, including some from the Kotlin team, take part in the Advent of Code challenges created by Eric Wastl. Advent of Code is a series of tasks published every December, which you solve and compete with others. Many would agree that it’s the best advent calendar to celebrate Christmas and New Year!

To help the community learn idiomatic Kotlin, and motivate more developers to solve Advent of Code tasks in Kotlin in the future, we decided to prepare solutions for the tasks from Advent of Code 2020. It doesn’t matter if you solved it back in December, you’re ready to solve it now, or you just want to check the solutions – we hope you’ll find something useful in these materials. Of course, it works best if you try to solve the same task first yourself!

Below is the solution and video for the first task. If you find this format useful and want us to cover more tasks in a similar fashion, please share in the comments!

Day 1. Report Repair

We’re fixing an expense report! Find the full task description at https://adventofcode.com/2020/day/1*.

You need to find the two (and in the second part, three) entries from the list of numbers that sum to 2020 and then multiply those two (or three) numbers together.

How to solve the task

Register at https://adventofcode.com/, open the task at https://adventofcode.com/2020/day/1, write your solution in Kotlin, and check the result on the site. You can either write Kotlin code online or using an IDE:

Finally, compare your solution with the solution below.

We marked the

src

folder as a source set to put the code directly there. We copied input files, like

src/day1/input.txt

, to the source folder for convenience. You can find the solutions in this project.

Solution

Here’s the sample input:

1721
979
366
299
675
1456

First, we need to read and parse the input. We can use the Kotlin

readLines()

function for reading a list of lines from a given file:

File("src/day1/input.txt").readLines()

The

readLines()

returns a list of Strings, and we convert it to a list of numbers:

import java.io.File
fun main() {
   val numbers = File("src/day1/input.txt")
       .readLines()
       .map(String::toInt)


}

You put this code inside the

main

function, the entry point for your program. When you start typing, IntelliJ IDEA imports the

java.io.File

automatically.

Now we can simply iterate through the list, and then for each number repeat the iteration and check the sum:

for (first in numbers) {
   for (second in numbers) {
       if (first + second == 2020) {
           println(first * second)
           return
       }
   }
}

You put this code inside

main

, so

return

returns from

main

when the required numbers are found.

In a similar way, you check the sum of three numbers:

for (first in numbers) {
   for (second in numbers) {
       for (third in numbers) {
           if (first + second + third == 2020) {
               println(first * second * third)
               return
           }
       }
   }
}

You can run it and get the result for a given input. That’s it! The first task is really a simple one.

However, we iterate over the same list again and again for each of the elements. Having two nested loops for finding two numbers makes it N2 operations, where N is the number of elements. When we need to find three numbers, that’s three nested loops, and N3 operations. If the list of numbers is large, that’s not the most efficient way to solve this type of problem. Surely there is a better way, right?

There definitely is and the Kotlin standard library can help us express that concisely. As often happens, we can replace the long calculation with some kind of smart storage used to find the result.

Solving the task for two numbers

First, let’s build a map for number “complements” – numbers that together with the given number sum up to 2020:

val complements = numbers.associateBy { 2020 - it }

We use the Kotlin

associateBy

function to build the map. Its lambda argument returns a key in this map, by which the list element is getting stored. For the sample input it’ll be the following map:

numbers: [1721, 979, 366, 299, 675, 1456]
complements map: {299=1721, 1041=979, 1654=366, 1721=299, 1345=675, 564=1456}

After this procedure, you can clearly see the answer! The very first number

1721

from the list is present in the

complements

map as a key:

1721=299

, which means it’s the complement for the number

299

, and they sum to

2020

.

Having stored this information in a map, we can check if any number from the list has a complement in this map. The following code finds the first number with an existing complement:

val pair = numbers.mapNotNull { number ->
   val complement = complements[number]
   if (complement != null) Pair(number, complement) else null
}.firstOrNull()

We transform each number into a pair consisting of the number and its complement (if the complement exists) and then find the first non-null result.

We use

mapNotNull

, which transforms each element in a list and filters out all the resulting

null

s. It’s shorthand for calling first

map

, and then

filterNotNull

on the result.

firstOrNull

returns the first element in the list or

null

if the list is empty. Kotlin standard library often uses the

OrNull

suffix to mark functions returning

null

on failure rather than throwing an exception (like

elementAtOrNull

,

singleOrNull

, or

maxOrNull

).

Starting with Kotlin 1.5.0, you can also replace the two consequent operations

mapNotNull

and

first(OrNull)

with one function call:

firstNotNullOf(OrNull)

.

After building the auxiliary structure, we managed to find the resulting two numbers in N operations, not in N2 as before!

We need a multiplication of these numbers, so here’s the last step:

println(pair?.let { (a, b) -> a * b })

The

pair

variable contains a nullable

Pair

of two numbers and is

null

if the initial list contains no numbers that sum up to 2020. We use safe access

?.

together with the

let

function and destructuring in a lambda syntax to display the result in case

pair

is not

null

.

Solving the task for three numbers

The next step is solving this problem for three numbers. Let’s reuse what we’ve done so far and extract the logic of finding a pair of numbers summing up to a given number into a separate function:

fun List<Int>.findPairOfSum(sum: Int): Pair<Int, Int>? {
   // Map: sum - x -> x
   val complements = associateBy { sum - it }
   return firstNotNullOfOrNull { number ->
       val complement = complements[number]
       if (complement != null) Pair(number, complement) else null
   }
}

We also used the

firstNotNullOfOrNull

function.

Now, we use

findPairOfSum

to build a helper map that stores the complement pair of values for each number which together with this number sums up to 2020:

// Map: x -> (y, z) where y + z = 2020 - x
val complementPairs: Map<Int, Pair<Int, Int>?> =
   numbers.associateWith { numbers.findPairOfSum(2020 - it) }

For the same initial input, here’s the complement pairs map:

numbers: [1721, 979, 366, 299, 675, 1456]
complement pairs: {1721=null, 979=(366, 675), 366=(979, 675), 299=null, 675=(979, 366), 1456=null}

As before, you already can see the answer! It’s the number that corresponds to a non-null pair in a map.

However, we don’t really need to build the whole map — we only need to find the first number that corresponds to a non-null pair! Let’s find it using the already familiar

firstNotNullOfOrNull

function:

fun List<Int>.findTripleOfSum(): Triple<Int, Int, Int>? =
   firstNotNullOfOrNull { x ->
       findPairOfSum(2020 - x)?.let { pair ->
           Triple(x, pair.first, pair.second)
       }
   }

Note Kotlin’s concise syntax – the function can return an expression directly.

The final step is to find the multiplication if the resulting triple is non-null, similar to how we did it before:

println(triple?.let { (x, y, z) -> x * y * z} )

That’s all!

In the next part, we’ll discuss how to solve the second task. Please let us know if you find this content useful and would like us to provide solutions for more tasks!

*Used with the permission of Advent of Code (Eric Wastl).

Continue ReadingSolving Advent of Code Puzzles in Idiomatic Kotlin

Kotlin API for Apache Spark 1.0 Released

The Kotlin API for Apache Spark is now widely available. This is the first stable release of the API that we consider to be feature-complete with respect to the user experience and compatibility with core Spark APIs.

Get on Maven Central

Let’s take a look at the new features this release brings to the API.

Typed select and sort

The Scala API has a typed

select

method that returns

Datasets

of

Tuples

. Sometimes using them can be more idiomatic or convenient than using the

map

function. Here’s what the syntax for this method looks like:

case class TestData(id: Long, name: String, url: String)
// ds is of type Dataset[TestData]
val result: Dataset[Tuple2[String, Long]] = 
        ds.select($"name".as[String], $"id".as[Long])

Sometimes obtaining just a tuple may be really convenient, but this method has a drawback: you have to select a column by name and explicitly provide the type. This can lead to errors, which might be hard to fix in long pipelines.

We’re trying to address this issue at least partially in our extension to the Scala API. Consider the following Kotlin code:

data class TestData(val id: Long, val name: String, val url: String)
// ds is of type Dataset<TestData>
val result: Dataset<Arity2<String, Long>> = 
        ds.selectTyped(TestData::name, TestData::id)

The result is the same, but the call is entirely type-safe. We do not use any strings and casts, and both the column name and the type are inferred from reflection.

We have also added a similarly reflective syntax to the

sort

function.

In Scala, this API supports arities up to 5, and we decided to be as consistent with the Scala API as possible. We also think that the usage of tuples with arities above five is an indication that something is going wrong. For example, maybe it would be better to extract a new domain object or, conversely, to work with untyped datasets.

More column functions

The Scala API is very rich in terms of functions that can be called on columns. We cannot make them identical to the Scala API because of the limitations of Kotlin. For example, overriding class members with extensions is forbidden, and the

Dataset

class is not extensible. But we can at least use infix functions and names in backticks to implement operator-like functions.

Here are the operator-like functions that we currently support:

  • ==
  • !=
  • eq / `===`
  • neq / `=!=`
  • -col(...)
  • !col(...)
  • gt
  • lt
  • geq
  • leq
  • or
  • and /

    `&&`

  • +
  • -
  • *
  • /
  • %

Luckily, we can see that very few of the functions require backticks, and those that do can be autocompleted without you having to type them.

More

KeyValueGroupedDataset

wrapper functions

We initially designed the API so that anyone could call any function that requires a

Decoder

and

Encoder

simply by using the magic

&lt;em&gt;encoder()&lt;/em&gt;

function, which generates everything automagically. It gave our users some flexibility, and it also allowed us not to implement all the functions that the

Dataset

API offers. But we would ultimately like to provide our users with the best developer experience possible. This is why we’ve implemented necessary wrappers over

KeyValueGroupedDataset

, and also why we’ve added support for the following functions:

  • cogroup
  • flatMapGroupsWithState
  • mapGroupsWithState

Support for Scala

TupleN

classes

There are several APIs in the Spark API that return

Datasets

of

Tuples

. Examples of such APIs are the

select

and

joinWith

functions. Before this release, users had to manually find an encoder for tuples:

val encoder = Encoders.tuple(Encoders.STRING(), Encoders.INT())
ds
    .select(ds.col("a").`as`<String>, ds.col("b").`as`<Int>)
    .map({ Tuple2(it._1(), it._2() + 1) }, encoder)

And the more we work with tuples, the more encoders we need, which leads to verbosity and requires us to find increasing numbers of names for new encoders.

After this change, code becomes as simple as any usual Kotlin API code:

ds
    .select(ds.col("a").`as`<String>, ds.col("b").`as`<Int>)
    .map { Tuple2(it._1(), it._2() + 1) }

You no longer need to rely on specific encoders or lambdas inside argument lists.

Support for date and time types

Work with dates and times is an important part of many data engineering workflows. For Spark 3.0, we had default encoders registered for

Date

and

Timestamp

, but inside data structures we had support only for

LocalDate

and

Instant

, which is obviously not enough. We now have full support for

LocalDate

,

Date

,

Timestamp

, and

Instant

both as top-level entities of dataframes and as fields inside of structures.

We have also added support for

Date

and

Timestamp

as fields inside of structures for Spark 2.

Support for maps encoded as tuples

There is a well-known practice of encoding maps as tuples. For example, rather than storing the ID of an entity and the name of the entity in the map, it is fairly common to store them in two columns in a structure like

Dataset&lt;Pair&lt;Long, String&gt;&gt;

(which is how relational databases usually work).

We are aware of this, and we’ve decided to add support for working with such datasets in the same way you work with maps. We have added the functions

takeKeys

and

takeValues

to

Dataset&lt;Tuple2&lt;T1, T2&gt;&gt;

,

Dataset&lt;Pair&lt;T1, T2&gt;&gt;

, and

Dataset&lt;Arity2&lt;T1, T2&gt;&gt;

.

Conclusion

We want to say a huge “thank you” to Jolan Rensen, who helped us tremendously by offering feedback, assisting with the implementation of features, and fixing bugs in this release. He worked with our project while writing his thesis, and we’re happy that we can help him with his brilliant work. If you want to read more about Jolan, please visit his site.

If you want to read more about the details of the new release, please check out the changelog.

As usual, the latest release is available at Maven Central. And we would love to get your feedback, which you can leave in:

Continue ReadingKotlin API for Apache Spark 1.0 Released

Adding Volume to the Kotlin Identity

Kotlin isn’t just a programming language, and it’s not just a language for Android development. It’s a whole ecosystem for creating various projects, from mobile apps and websites to data science. It’s an active, friendly community, with hundreds of online and offline events happening all around the world.

Despite its fairly long history, Kotlin has never had a consistent visual style. Designs were created for specific events or to solve a particular problem, with only the Kotlin flag remaining constant. When creating new assets, we’ve had to reconsider what we already have, which has made the design process more complicated than it should be.

We wanted to create a brand identity that would be robust, iconic, and recognizable, and at the same time multifaceted and easily used.

The colors felt outdated and the whole impression we got from the logo was at odds with a modern, growing programming language. Although the stripe in the logo did help it to stand out and make it recognizable, it wasn’t a very unique look, and so we decided to remove it. We took our flag, removed the stripe, and mixed its colors to make them brighter and more vibrant. That’s basically the story of how we came up with our awesome new logo – the gradient filled flag.

As a font, we’ve picked JetBrains Sans. As one of JetBrains’ products, we wanted to emphasize our relationship and our commitment to the same values. As a fun detail, there is an alternative “i” in the font, specifically for the word Kotlin.

Brand identity is a versatile tool that can be used across a broad spectrum of situations. We’ve picked 3D shapes for the basis of Kotlin’s visual language, as they can be used in many ways. These shapes can be used as shapes, masks for photos, icons, and so much more.

We’ve now managed to successfully implement the visual style into our product pages and social media, made two online events, and are in the process of planning the next one! We already see that our communications are becoming more consistent, leading to a more robust brand. And what’s more, the design team can now deliver everything that much faster.

Art director:
Vladimir Grigoriev

Designers:
Alexey Salmin
Denis Voronov

Continue ReadingAdding Volume to the Kotlin Identity

Kotlin Features Survey Edition #2

We’re planning the next steps for Kotlin’s development and would like to know your opinion about the relative importance of upcoming features. To this end, we’re launching the Kotlin Features Survey, along with a webinar hosted by Roman Elizarov and Svetlana Isakova. 

The survey provides an opportunity for you to have your say about the features that should be prioritized in Kotlin. You can find the descriptions of all the features and their use cases below. During the webinar you will be able to ask your questions and discuss the future of the language. 

Go to the surveyRegister for the webinar

One of the goals of Kotlin, as a pragmatic language, is to stay both concise and versatile at the same time. That’s why designing new features is always hard. New features bring new functionality, but they can complicate the language and provide an easy opportunity for misuse.

The use cases of our users are our brightest beacon in this ocean of choices and trade-offs, helping us navigate and make the right language design decisions. Features that address popular use cases are a lot more likely to be implemented first.

Go to the survey

Webinar with Roman Elizarov and Svetlana Isakova

The webinar will take place on July 13, 17:30 CEST. 

This is a great chance to get answers to any questions you have about the features before going through the survey. You will still be able to take the survey after the webinar.

Register for the webinar

Helpful materials

If you’re interested in learning more about the Kotlin design process and your opportunities to contribute to it, take a look at these materials:

Feature descriptions and instructions 

Below, you will find the descriptions of features and the use cases they address. To vote, please fill out the form

We ask you to choose the three features that would bring the most benefit to you, and downvote one that will create more problems for your code. We also ask you to vote for specific solutions, as some of the potential features can be implemented in multiple ways and in some aspects they may contradict one another. Your votes will help us a lot!

Please note that this is not an exhaustive list of features we are currently working on. The listed features are also at various stages of development. We cannot promise that any of these features will make it into the language any time soon, even if they receive a lot of votes. You also won’t see any of them in version 1.6, as your votes help us to prioritize our work in the longer term.

Go to the vote

Companion objects and static extensions

Problem: Suppose that we want to define the

String.CASE_INSENSITIVE_ORDER

property that is available on the String class directly (not on a String instance). In Kotlin, you can have an extension to the class if, and only if, the corresponding class declares a companion object.

// Works because String defines companion object:
val String.Companion.CASE_INSENSITIVE_ORDER: Comparator<String>
   get() = ...

You cannot add a similar extension to a third-party class that does not have a companion object.

Related problem: In libraries, there’s often the need to group a number of declarations together so that they can be used with a common prefix. Well-known examples include

Delegates.notNull

in the standard library and

Dispatchers.Default

in

kotlinx-coroutines

. The only mechanism for this that Kotlin currently provides is the object (companion or not). However, exposing an object instance is a significant maintenance commitment for a library when no instance is actually needed.

Another related problem: Inside declarations of your own classes, companion objects represent the only way to declare static members that are private to the class, which adds additional overhead to the JVM bytecode, especially when compared with the static methods in Java.

Possible solutions:

  1. Do not do anything conceptually new, as that would complicate the language. Instead optimize the JVM bytecode for private companion objects where possible.
  2. Introduce static members like in Java / C# / JavaScript with some sort of static extension syntax. This would make it possible to write static helper functions closer to where they are needed, without having to group them together. Companion objects will remain supported for cases where they need to extend a class or implement an interface.
  3. Introduce the new concept of a namespace – a kind of ephemeral object without an instance that every class automatically possesses, so that an extension to a class’s namespace can be introduced to any third-party class, and namespace members are naturally compiled down to static members on the JVM. This keeps static helpers grouped together in the source, but removes all the object overhead.

More details: KT-11968 

Multicatch and union types

Problem: When working with exception-heavy Java APIs in Kotlin, you cannot catch multiple exception types in a single catch() clause. You’ll have to write multiple catch() clauses or use a

when

expression to test the exception type: 

fun loadData(path: String) {
   try {
       findDataLoader(path).loadXmlData()
   } catch (e: IOException) {
       println("Failed")
   } catch (e: JDOMException) {
       println("Failed")
   }
}

Related problem: When writing Kotlin APIs that can succeed or fail in various ways that yield additional information, you’d use a sealed class as a result, providing a type-safe listing of all possible types of errors that a function can fail with. This can become quite verbose too:

  
sealed class ParseResult {
   object Empty: ParseResult()
   data class Data(val data: Data, val status: Status): ParseResult()
   data class Failure(val errorDetails: ErrorDetails): ParseResult()
}

fun String.parseData(): ParseResult

Possible solutions:

  1. Don’t do anything specifically for exceptions because modern Java APIs don’t abuse exceptions that much (and older ones will eventually be superseded), but provide a concise enum-like syntax for declaring sealed classes, so that Kotlin-style error-returning functions are easier to write.

We could have a simpler syntax for the cases that are “between” enums and full-power sealed classes, which would be equivalent to enums if all the options are constants:

 
  sealed class ParseResult {
    Empty, // just like enum entry
    Data(val data: Data, val status: Status),
    Error(val errorDetails: ErrorDetails)
}
  

That would make it possible to implement various optimizations, like efficiently implementing constants without separate classes and generating the correct toString for them right away.

  1. Add support for union types, so that it is possible to declare a function returning one of the possible values without having to introduce a dedicated ParseResult type at all:
 
fun String.parseData(): Data | Failure

This also provides a syntax for multicatch when working with legacy Java APIs:

 
fun loadData(path: String) {
   try {
       findDataLoader(path).loadXmlData()
   } catch (e: IOException | JDOMException) {
       println("Failed")
   }
}

More details: KT-7128

Kotlin properties overriding Java accessors

Problem: Kotlin properties can’t override Java-style getters and setters:

  // Java
public interface JNamed {
   String getName();
}
// Kotlin
// Doesn't work:
class KNamed(override val name: String): JNamed

A Kotlin property can’t override Java’s

getName()

method (or the

name()

method in a Java record). This is because Kotlin sees a Java interface with the

getFoo()

method as having a member function

getFoo()

(which can be overridden) and an extension property

foo

for idiomatic access from Kotlin (which cannot be overridden).

Related problem: In multiplatform projects, Kotlin’s expect

val/var

cannot be provided by an actual Java function:

  // Interface.common.kt
expect interface I {
   val type: Int
}
// Interface.jvm.kt
// Doesn't work:
actual typealias I = JavaInterface

// JavaInterface.java
public interface JavaInterface {
   public int getType() {
       return 42;
   }
}

Supporting this feature could greatly simplify the process of adding idiomatic common Kotlin modules for Java libraries.

Possible solution:

Attempts to override Java functions with Kotlin properties could be supported (as opposed to producing an error). This could cause a number of weird cases in mixed Java/Kotlin hierarchies (e.g. when one interface overrides a Java

getFoo()

function with a Kotlin

getFoo()

function, while another interface overrides it with a Kotlin

foo

property). All those cases could be detected during compilation to report errors, and various JVM-specific annotations could be provided to explicitly specify what is supposed to happen in such cases and how to work around the corresponding errors. 

More details: KT-6653

Package-private visibility

Problem: In Kotlin, you can limit the scope of declarations to either a file (which is usually small) by using a

private

modifier or to a whole module (which tends to be big) by using an

internal

modifier. There’s no intermediate scope that allows a group of related files to share their implementation details, like package visibility in Java does. Packages in Kotlin are simply namespaces: they provide “full names” to their content declarations but have no visibility restrictions.

  package foo

// No way to define a package-private function:
/*package-private?*/ fun bar() { ... }
 

Instead, Kotlin has internal visibility, which means a declaration with this visibility modifier is “visible inside a module”. 

Still, many developers would like to have package-private visibility for their mixed Java / Kotlin projects.

Possible solutions:

  1. Introduce a new Java / C#-like visibility modifier that sits somewhere in between
    internal

    and

    private

    (limited to the same package in the same module).

  2. Don’t add any new visibility, but instead introduce a way to mark a package as “confined / isolated”, so that all
    internal

    declarations in the package become visible only to this package. 

More details: KT-29227

Multiple receivers on extension functions and properties

Problem: In Kotlin, you can define an extension function inside a class. A member extension function inside a class has two receivers – a dispatch receiver from the class and an extension receiver from the extension member:

class View { // View is a dispatch receiver
   val Float.dp // Float is an extension receiver for dp property
       get() = this * resources.displayMetrics.density
      // this refers to Float
      // resources, or this@View.resources, is a property in View
}

You can only call the

dp

property within the scope where View is available as a receiver:

  with (view) {
   42f.dp
}

It would be useful to be able to define such a member, not only as a member function or property but also outside of the context class (

View

). That would make it a function or property with multiple receivers (in this example, with two receivers:

View

and

Float

). When the context class belongs to a separate library, it would be the only option.

Possible solution:

We could define a function or property to be used only in contexts where a specific receiver is available:

  context(View)
val Float.dp
   get() = this * resources.displayMetrics.density

When you use the

dp

property, you would still need to provide

View

as an implicit receiver, as before. But now it wouldn’t need to be a member of the

View

class.

You could define a top-level function requiring scope. For example, you could inject a logger, or specify that an operation can be performed only in the context of the database transaction:

context(Logger)
fun performSomeBusinessOperation(params: Params) {
  info("Operation has started")
}
context(Transaction)
fun updateUserSession() {
    val session = loadSession()
    session.lastAccess = now()
    storeSession(session)
}

You wouldn’t be able to call such a top-level function as an extension on its context receiver (logger can’t become an explicit receiver). The function could only be called “in the context with this receiver”, but the receiver wouldn’t become the main object operated on, as is the case with extensions.

You could define several context receivers, as well as ones with generic parameters. Read the detailed description of this functionality’s design, including the reasoning of how to conceptually distinguish between use cases of context receivers and regular extension receivers in the corresponding “Context receivers” KEEP.

More details: KT-10468, KEEP-259

Default implementations for expect declarations

Problem: Currently, expect declarations in the common code can’t have default implementations. Functions, properties, and member functions inside an

expect

class can’t define bodies. It’d be useful to provide default implementations in the common code and tweak them in the platform code if needed. 

Possible solution direction:

Allow specifying default implementations for

expect

declarations.

More details: KT-20427

Overloadable bitwise operators like I and &

Problem. Bitwise operators like

|

(or),

&amp;

(and),

&lt;&lt;

(left shift),

&gt;&gt;

(right shift),

^

(xor, “exclusive or”), and

~

(tilde, or “bitwise negation”) are used mainly for bit-manipulation code.

In Kotlin, you use them by writing their explicit names:

out = out or (inByte and 0x7F) shl (bytes++ * 7)

While this is more readable for developers unfamiliar with bit manipulation practices, many people who are used to writing such code in other languages find this approach too verbose and limiting.

Possible solution:

Add the corresponding operators to make this code familiar for people who often write bit-manipulation code:

  out |= (inByte & 0x7F) << (bytes++ * 7)

Following the general Kotlin philosophy, these operations would become overloadable operators in Kotlin, also to be used with Number types from different libraries. But it’s also important to consider that this could make it too easy to misuse them in DSLs and APIs.

More details: KT-1440

Name-based destructuring

Problem. Position-based destructuring works great for “set in stone” library classes like Pair or Triple, but it presents a danger when working with custom data classes:

  data class Address(
   val street: String,
   val city: String
)
val (myStreet, myCity) = myAddress
  

If we later modify the class and add the property

postalCode

in the second position (between street and city), the Kotlin code will continue to compile. But it’ll contain a bug: because of the positional destructuring, the

postalCode

data will be assigned to the

myCity

variable.

In addition to this, positional destructuring does not scale to real-life entities that can have dozens of properties, of which a particular piece of code may only need to retrieve a few.

Possible solution:

That’s a known problem when using positional destructuring with custom data classes, and name-based destructuring should solve it:

  val (street = myStreet, city = myCity) = myAddress

You would then refer to properties by names, not by their positions. If you add a new

postalCode

property, the code would continue to work correctly and assign the right property to the

myCity

variable.

If we introduce the new named destructuring syntax, we would be able to consider making the positional destructuring of data classes an opt-in feature (with some kind of positional class modifier) and stop offering it by default for all data classes.

More details: KT-19627

Collection literals

Problem. In Kotlin, you create collections by calling library functions, such as

listOf(1, 2, 3)

or

mapOf(“one” to 1, “two” to 2, “three” to 3)

. Such invocations look verbose for data-heavy applications. What’s more, the general creation of collections is currently inefficient in Kotlin due to the underlying use of

varags

.

Possible solution:

We could make collection creation more concise and convenient by having a specialized syntax:

val list = [1, 2, 3]
val map = ["one": 1, "two": 2, "three": 3]

By default, such literals would create a read-only

List&lt;Int&gt;

and

Map&lt;String, Int&gt;

, but if you need different types, you should be able to specify them explicitly or have them inferred from the context:

val set = Set<Int> [1, 2, 3]
val map: MutableMap<String, Int> = ["one": 1, "two": 2, "three": 3]

The construction of collection literals would be efficient by design. The underlying mechanism would not rely on varargs, pairs, or other wrappers.

As usual, our goal is to provide a general solution, not a built-in syntax for a specific use case. We need a solution that works for library types, as well as for user-defined types. The syntax above is supposed to be desugared into something like:

val set = Set.build(3) { add(1); add(2); add(3) }
val map = Map.build(3) { put("a", 1); put("b", 2); put("c", 3) }

A user-defined collection type would have an option to provide a similar kind of “collection builder operator” that is recognized by the compiler and used under the hood.

More details: KT-43871

Structure literals (Tuples)

Problem: Kotlin doesn’t support tuples, finite ordered sequences of elements, which are a common feature in most modern languages. If you need to return several values from a function, you should either explicitly use the library classes

Pair

or

Triple

, or define a new data class for this case. While defining a separate class is generally a better solution from the API design perspective, it might feel too verbose.

Related problem: It would be convenient to be able to create data classes without calling constructors explicitly, especially for data-heavy scenarios, or when your data class has many optional constructor parameters. Compare the following two declarations (assuming we can use collection literals syntax […]):

val points: List<Point> = [Point(10, 20), Point(20, 40), Point(30, 60)]
val points: List<Point> = [(10, 20), (20, 40), (30, 60)]

The second one reads much better when you need to create lots of

Points

in the code.

Possible solution:

Introduce structure literals. You could explicitly specify their types:

val point: Point = (10, 20)
val pair: Pair<Int, String> = (12, "abc")

Without explicit specifications, the types of tuples could represent anonymous data classes (or value classes, when they become supported). The component names could be either explicit or implicit, and you could omit the type, so the following variable would get an anonymous tuple type (Int, Int):

val pair = (10, 20)

Tuple types should support naming their components, so (

x = Int, y = Int

) is different from (

first = Int, second = Int

).

Then you would be able to use tuple types for the ad-hoc returning of multiple values from a function:

fun parseNextInt(line: String): (value: Int, nextPosition: Int)

However, in order to assign this anonymous tuple value to a real type, the compiler would need to perform under the hood “conversion”. That poses new challenges. For instance, you would potentially observe weird and unintuitive behavior if a value were implicitly converted and became not equal to its original value (it’s no longer possible to look it up in an ordinary collection). Allowing user-defined conversions would add an additional non-trivial thing to learn and understand, amounting to a potential “overcomplication” of the language.

More details: KT-8214, KT-45587

Operator and DSL improvements

Problem: Kotlin supports overloading for a restricted set of commonly used operators. This and other features (like extension lambdas) play a big role in improving APIs and creating DSLs. Sometimes, however, the exact operator conventions seem too restrictive, and there are different requests for extending them.

One of the most common requests is to upgrade the convention for comparison operators to allow the

compareTo

method return a custom type instead of

Int

. Currently, the replacements must be defined as functions named

lt/le

or

less/lessEq

:

  MyTable.select { MyTable.ordersNumber lessEq limit }

Since

equalTo

can’t return a custom type, these comparisons are defined as

infix

functions with explicit names:

infix fun <T : Comparable<T>> Expression.lessEq(t: T): ComparisonOp
infix fun <T : Comparable<T>> Expression.less(t: T): ComparisonOp

Possible solution:

Review the current list of conventions, trying to extend them when possible (of course, this can only be done in a backward-compatible way), so that this and similar requests could receive better support in the corresponding DSLs and frameworks. Like using

&lt;=

directly:

  MyTable.select { MyTable.ordersNumber <= limit }

However, for consistency, this would mean that the equality comparison operator

==

would also be allowed to change its meaning for certain types in certain contexts, and that it would potentially return something other than a Boolean value in those cases. 

More details: KT-45666, KT-17307

Lateinit for nullable and primitive types

Problem: In Kotlin, a

lateinit

property can be neither nullable nor of a primitive type, like

Int

or

Long

. This restriction is connected with the underlying implementation: the “uninitialized value” is stored as

null

. However, this restriction is often seen as artificial, surprising, and inconvenient. Why not simply allow

lateinit

to be nullable and allow

lateinit

primitives?

The reason is that

lateinit

in Kotlin/JVM serves the dual role of being used as an injection point for DI (dependency injection) frameworks and also of exposing the underlying field as a public field on the JVM for those frameworks.

Possible solutions:

  1. Lateinit primitives could be implemented by boxing, or by using an additional bitmask. For nullable types, the compiler can’t use
    null

    as an uninitialized marker, but it could use another internal

    UNINITIALIZED

    value instead and check it on each access (similar to how it currently works with

    nulls

    ). However, using another special value modifies the type of the property and therefore would complicate the scenarios of using nullable

    lateinit

    properties for dependency injection frameworks (the proper type of nullable field would no longer be recognized by those frameworks, causing strange errors).

  2. Decouple lateinit into two separate features. Leave it alone for DI frameworks (with current limitations on non-nullity and non-primitiveness) with potential deprecation and replacement in the future. Focus on optimizing simple delegated properties, so that
    var property by Delegates.lateInit()

    is efficiently compiled, supports all Kotlin types, and can be used in all non-DI use-cases as a replacement for

    lateinit var property

    .

More details: KT-15284

Having “public” and “private” property types

Problem: We often store values in a

MutableList

property, and use this property inside the class as a

MutableList

, but what if we want to expose it outside the class as a read-only

List

?

  private val items = mutableListOf<Item>()
   public get(): List<Item>
  

Possible solution:

We could explicitly have

public

and

private

types for the same property!

Annotation to warn about the unused return value

Problem: Sometimes the result of a function call shouldn’t be ignored. If you forget to use this result in some way, it’s a straightforward indication of an error. One example is not assigning the result of adding an element to a list somewhere:

val list = listOf("a")
list + "b"

The initial list hasn’t changed, and the list returned by

+

is lost. Another “trap” is forgetting to call a terminal operation on

Sequence

.

One specific case where it’s a mistake to ignore the result is pure functions. A function is called pure if its result depends on the arguments only and doesn’t operate over the global state. Such a function can only use other pure functions and constants.

One more example is the result of an async-style function, which returns

Deferred

or another kind of “future” (“promise”) object. If you never access the

Deferred

value afterward and its computation ends up with an exception, that exception is going to be ignored. 

Possible solution:

Introduce an annotation on types indicating that the value can’t be ignored and should be somehow used. The exact name still needs to be discussed – for now, let’s call it

CheckResult

. The

async

and

future

functions from the

kotlinx.coroutines

package could return a value of

@⁠CheckResult Deferred

type, and the compiler could emit an error if your function returns a value that you never use.

Possible annotation names (you can indicate your favorite if you vote for this feature):

  • CheckResult
  • CheckReturnValue
  • NoDiscard
  • MustUseReturnValue
  • MustUse

An alternative would be to “swap the default”:

  • Start emitting the compiler warnings if the result of any function is ignored.
  • Introduce an annotation (like
    DiscardableResult

    ) to mark the functions for which dropping the result is fine.

More details: KT-12719, KT-22507

Unqualified enum constants & sealed subclasses in when expressions

Problem: Referring to

enum

constants with fully qualified names like

Color.BLUE

often looks verbose. Of course, there is an alternative – importing constants by name. But then they become available in the whole file and not just inside the given context like a

when

expression:

package mypackage
import mypackage.Color.*

// Available by short names in the whole file:
enum class Color { RED, GREEN, BLUE }
fun test(color: Color) = when (color) {
   RED -> 1
   GREEN -> TODO()
   BLUE -> TODO()
}

What’s more, the import trick may not work with short universal constant names like

ON

and

OFF

, since it can result in ambiguous names.

Related problem: When enumerating all the subclasses of a sealed class, you also have the option of either importing the subclasses explicitly or using the qualified names:

sealed class UserResult {
    data class Found(val user: User) : UserResult()
    data class NotFound(val reason: ErrorCode) : UserResult()
}
fun processResult(result: UserResult) = when (result) {
    is UserResult.Found -> ...
    is UserResult.NotFound -> ...
}

That’s verbose, too.

Possible solution:

Support context-sensitive resolution for names. The compiler could resolve the when branch condition in the context with the expected type, specifically, with the type of the

when

argument:

// Only available by short names inside when:
fun test(color: Color) = when (color) {
   RED -> ...
   GREEN -> ...
   BLUE -> ...
}

The same applies to

sealed

subclasses:

fun processResult(result: UserResult) = when (result) {
    is Found -> ...
    is NotFound -> ...
}

The use-cases for context-sensitive resolution are not limited to enums, sealed classes, and the context of when expressions. It works whenever there’s an expected type:

// Works without imports:
val color: Color = RED
widget.color = RED
fillWithColor(RED)

This could greatly simplify repetitive code like modifier

= Modifier.foo()

when resolving this Modifier method in the context of the parameter type (with the

Modifier

expected type):

Image(
    image, 
    // Currently, you write modifier = Modifier.preferredHeightIn()
    modifier = preferredHeightIn(maxHeight = 180.dp).fillMaxWidth()
) { ... }

More details: KT-16768

Continue ReadingKotlin Features Survey Edition #2

Kotlin Multiplatform Mobile for AppCode

Hi everyone,

Today we’re expanding our Kotlin Multiplatform Mobile ecosystem by introducing the Kotlin Multiplatform Mobile (KMM) plugin for AppCode.

By using this plugin you can:

  • Specify the structure of the iOS part of your KMM project in the same way you specify the Android one. We’ve created a special Gradle DSL for describing iOS projects that does not require having
    .xcodeproj

    or

    .xcworkspace

    .

  • Write Objective-C/Swift and Kotlin code in one IDE. Cross-resolve between Objective-C/Swift and Kotlin works automatically so you can easily jump from the common Kotlin code to its usages in the iOS-specific part. It goes without saying that code completion and code highlighting for Objective-C/Swift are available as well.

The goal is to maintain the project structure in one place and unify the layout of iOS and Android project parts, while preserving all the flexibility that Kotlin Multiplatform Mobile provides.

Pre-requisites

In addition to Xcode, which is required to work with the iOS part, you will need to download the Android SDK. It should be automatically identified by the plugin and set in

local.properties

:

Android SDK directory

Create a project

In the

File | New Project

wizard, select the KMM Application template:

New project

AppCode will create a minimal project with iOS and Android “Hello world!” applications. As you can see, the whole iOS application is inside the apple folder. The following minimal DSL is used to describe the project in the corresponding

build.gradle.kts

:

apple {
    iosApp {
        productName = "mpptest"

        sceneDelegateClass = "SceneDelegate"
        launchStoryboard = "LaunchScreen"
      
        dependencies {
            implementation(project(":shared"))
        }
    }
}

Here we have some predefined properties, such as

productName

or

scenedelegateClass

in the

iosApp

target, that speak for themselves. We can also specify any custom build settings or product info keys in the following way:

productInfo["NSAppTransportSecurity"] = mapOf("NSAllowsArbitraryLoads" to true)
buildSettings["OTHER_LDFLAGS"] = "some_value"

Write the code

The KMM plugin for AppCode provides the same code assistance as the Kotlin/Native plugin did previously. Cross-resolve between Objective-C/Swift and Kotlin works automatically and does not require rebuilding the project:

Code assistance

Build, run, and debug

You can run and debug the Android or iOS part of your application by selecting the corresponding run configuration:

Run configurations

To build the iOS part, AppCode generates a temporary Xcode project and invokes the Xcode build system, so you can be sure that our Gradle project model will work the same as a regular Xcode project does.

If you set a breakpoint inside the shared code or the platform-specific part, the IDE will recognize them and will stop during debugging:

Debug

Test your app

The following test configurations are created automatically:

  • Common Tests – for tests in the commonTest module. We can run them on the local JVM (since unit tests for Android do not have to be run on the simulator) or one of the iOS devices/simulators (since even unit tests on iOS are executed in the simulator).
  • Android Library Tests – for tests in androidTest package (platform-specific tests written in Kotlin for Android).
  • iOS Library Tests – for tests in the iosTest package (platform-specific tests written in Kotlin for iOS).
  • iOS App Tests – for tests in the iosAppTest package, where any iOS tests can be stored.

You can run and debug them (as well as separate tests):

Tests

Supported platforms

Currently, only iOS application and test targets are supported. Android targets are supported in the same way as in Android Studio.

Future plans

We’re working on a solution that will allow you to hook up an existing

.xcodeproj

or

.xcworkspace

to the Gradle DSL to make it possible to easily connect existing Xcode projects with the common and Android parts.

We’re also considering integrating some platform-specific IDE functionality. But as a first step, we’re focusing on the language support and project model management to give you a first-class experience for writing your business logic.

That’s it! We hope this new plugin will help you develop your applications easier and faster. You can try it with AppCode EAP builds that are already available for download.

Should you have any feedback for us, don’t hesitate to use the comments section below, submit an issue or feature request to the plugin tracker, or ping AppCode support. Thank you!

Download KMM plugin for AppCode

Continue ReadingKotlin Multiplatform Mobile for AppCode

Calling All Kotlin Programmers! Take Part in Kotlin Heroes: Episode 7 Coding Contest

If you’ve been thinking of new ways to level up your Kotlin coding skills, programming competitions are a great way to do so.

Starting from June 22, you can polish your Kotlin skills and practice solving algorithmic problems in a dedicated practice round on the Codeforces platform. On June 29, the real challenge begins – take part in Kotlin Heroes: Episode 7, and compete to win prizes!

I like a challenge, sign me up!


Even if you’ve never participated in programming contests before, we encourage you to give it a try. This is a great opportunity to test your limits and improve your coding.

The tasks of this episode are thought-provoking, fun, and designed to suit programmers of all levels. And everyone has a chance to win prizes!

It’s easy to take part

  1. Register on the Codeforces platform.
  2. Register for the Kotlin Heroes: Episode 7 contest. 
  3. Mark your calendar: June 29, 2021, at 14:35 UTC.
  4. Show up at the appointed time and enjoy the competitive spirit!

Take Part in Kotlin Heroes!


How to prepare

Enroll in the practice round with open solutions, which starts on June 22, 2021, at 13:35 UTC and lasts for a week. In this round, all solutions are open and you can see hints if you get stuck. 

Refresh your knowledge of Kotlin and learn more about competitive programming:

Contest rules

  • The contest features a set of up to 10 problems that range from simple ones, designed to be solvable by anyone, to hard ones, to make it more interesting. You are only allowed to use Kotlin to solve these problems. You have 2 hours and 30 minutes to complete the tasks. You move up the leaderboard by finding the most efficient solutions to the problems in the shortest time possible.

Prizes

  • The top three winners will receive prizes of $512, $256, and $128, respectively. 
  • The top 50 contestants will win an exclusive Kotlin Heroes t-shirt and Kotlin stickers. 
  • Every competitor who solves at least one problem is entered into a drawing for one of the 50 exclusive Kotlin Heroes t-shirts.

I like a challenge, sign me up!


Good luck and enjoy Kotlin!

Continue ReadingCalling All Kotlin Programmers! Take Part in Kotlin Heroes: Episode 7 Coding Contest

Answering the Top 10 Questions About Kotlin’s Future

In this post you’ll find the answers to 10 of the most upvoted questions about Kotlin’s future. You can also jump into the discussions in the comments or on Reddit (links below)!

A couple of weeks ago, as part of the 1.5.0 release event, we hosted a Kotlin team AMA on Reddit. It was exciting to receive hundreds of questions from all of you, and it was also great fun to answer them and participate in all the discussions.

When going through the questions on Reddit, we felt it was important to share them more widely. The answers are a great source of knowledge for everyone in the Kotlin community. Thanks a lot to everyone who asked the questions and to the team who carefully answered all of them. 

We received quite a few technical questions, as well as questions about the recent release. But upvoted questions, and in fact the ones we found most interesting ourselves, were about Kotlin’s future.

Questions

Question #1
Some years ago, there was a developer survey letting us vote for our most-wished features. Will there be something similar in the future? 

“Yes, at the moment we are working on the next edition of this survey. Stay tuned!” 
The thread on Reddit

Question #2 
Over the medium to long run, lets say 5 years or so, given the pace of change for Java has picked up do you envisage any problems in maintaining the compatibility of Java and Kotlin? For example are there currently any projects for changing the Java language or JVM bytecode structure which could be really difficult to integrate Kotlin with? 

“We closely follow the JVM design process, and we don’t see ourselves running into problems with any JVM features that are in development, or even with the ones that are planned for the far future.” 
The thread on Reddit

Question #3 
I asked this last time but what’s your current thinking regarding pattern matching?
 

“We are keeping it on our radar, but we feel that the most important short-term improvement that we can make to Kotlin

when

expressions is adding support for guards. See https://youtrack.jetbrains.com/issue/KT-13626”. 
The thread on Reddit

Question #4 
Will there be a way for Kotlin to compile to Swift instead of Obj-C for KMM? 

“Kotlin is not actually compiled to Objective-C for iOS.

Instead, at its core, Kotlin/Native compiles Kotlin directly to the native code (with the help of LLVM).

On top of that, it generates bridges to make the compiled Kotlin code accessible from Objective-C and Swift. For a Kotlin class

Foo

, the compiler additionally generates an “adapter” that Objective-C and Swift perceive as an Objective-C class. This adapter delegates everything to the original Kotlin class.

So, it’s more like the Kotlin/Native compiler adds an Objective-C representation for the Kotlin API. The question really is: will there be a way for Kotlin to add a Swift representation? And the answer is: this is on our radar.” 
Read more on Reddit

Question #5
Are there plans to extract the formatting feature from Intellij [IntelliJ IDEA] into a separate tool? 

“We are close to finishing our work on a formatting rules checker that will run on CI. It will be based on Qodana and launch an inspection that IntelliJ IDEA now has for Kotlin.

Currently:

  • This inspection relies too heavily on IntelliJ IDEA’s formatting settings that are stored in the .idea folder.
  • It’s not currently very informative – it just says that formatting is wrong.

We’ll hopefully address these two issues and add the inspection to Qodana.” 
Read more on Reddit

Question #6 
Previously you announced that compilation speed would be the top priority for the Kotlin team, but that it would be a multi-year effort spanning several releases. Any updates you can share on how things are shaping up?

“Our  work on compilation performance is split into two parts:

  1. Writing a new compiler frontend, with the main goal being much better performance than the existing one.
  2. Optimizing the new compiler backend (backend IR) once it is released and the most critical bugs are fixed.

The JVM/IR backend was released in Kotlin 1.5, so our JVM team will be focusing on its performance in the near future.

Meanwhile, the new frontend is actively being developed, and we want to release an early version this fall. The new compiler frontend will provide the main performance boost. At the moment, it is 4 times faster than the current frontend, which effectively doubles the speed of the full compilation pipeline (frontend + backend).

Also, since the Kotlin IDE plugin uses a compiler plugin for analysis, a new plugin based on FIR will give a performance boost for the IDE experience, too.” 
The thread on Reddit

Question #7 
Are there any plans to offer a Kotlin DI [Dependency Injection] solution natively?
 

“We feel that DI [Dependency Injection] works better as a separate library, as it makes too many competing demands for it to ever be integrated into the language or even into its standard library.”
Read more on Reddit

Question #8 
How does the idea of a Kotlin / TypeScript cross compiler sound to the Kotlin team? Similar to how Kotlin and Java can be compiled together.

“We definitely agree it would be great to have such seamless interop with JS/TS, but it’s challenging for the following reasons:

  • Kotlin and TS/JS Type systems and semantic incompatibilities.
  • TS evolves very quickly.
  • We would need to somehow integrate in both directions(!): a TS compiler written in TS and targeting the JS VM as a run environment, and a Kotlin compiler written in Kotlin and Java and targeting the JVM as a run environment. Alternatively, we could write and support our own TS compiler frontend 🙂
    (For Java it’s much simpler – we just reuse IntelliJ’s Java compiler frontend)

So, for now we are focused on a separate multistage interop – consuming TS declarations (d.ts) by converting them to Kotlin declarations and then generating TS declarations (d.ts) from Kotlin. These Kotlin-generated TS declarations (d.ts) are only available inside the new JS IR compiler backend.

Who knows, maybe someday it also will be seamless, as we’d all like ;)” 
Read more on Reddit

Question #9
What’s next for Kotlin and Spring, specifically from server side Kotlin? 

“Spring Boot 2.5 has just been released with a Kotlin 1.5 baseline and the upgraded Coroutines 1.5 and Serialization 1.2 libraries.

Our next steps are providing great Kotlin/JVM/Native (Native with Kotlin/JVM via GraalVM native images) support via https://github.com/spring-projects-experimental/spring-native/, empowering multiplatform development (with the Kotlin/JS frontend for example), translating Spring Boot documentation to Kotlin (via a contribution from the Kotlin team), and making sure that some APIs that are currently broken with Kotlin due to some type-inference bugs with recursive generic types (like WebTestClient) become usable.

Longer term, my favorite topics are getting rid of kotlin-reflect at runtime in Spring Framework by performing Kotlin reflection ahead of time, and continuing to mature https://github.com/spring-projects-experimental/spring-fu for a more DSL-ish way of configuring Spring Boot.

Even if it’s not a Spring based implementation, we would like to help https://github.com/rsocket/rsocket-kotlin, which is a great Kotlin multiplatform implementation of the RSocket protocol (https://rsocket.io/ can be an alternative to GRPC in a lot of use cases) mature.”
Read more on Reddit

Question #10 
It was planned to provide meta programming capabilities – any news on this topic?

“We’re working on meta-programming on two fronts:

More helpful resources about Kotlin and its future 

Subscribe to our Twitter, blog updates, and YouTube channel for future announcements. Soon we will launch the vote for new features in Kotlin, and we’re looking forward to hearing your take on what we should prioritize!

Continue ReadingAnswering the Top 10 Questions About Kotlin’s Future

Simple Steps for Improving Your IDE Performance

This blog post continues from the previous one about how we measure IDE performance and what results we’ve already achieved. We want to give you a few tips on what to do if you are still experiencing poor performance and show you how best to approach us with issues that bother you.

Features and solutions

Shared-indexes

Shared indexes were introduced in IntelliJ IDEA 2020.2. The goal was to reduce the time spent on project indexing by up to 75%. Now, you don’t need every computer to process and index the same external project dependencies, or all of your project if you are only working on a small part of it. This system lets you create a local storage for indexes, and connect to a global storage where, for example, JetBrains keeps indexes of the JDKs. To set up this feature for your project, check out these instructions.

IntelliJ LightEdit mode

We have all had times when we have just wanted to quickly look into a file, correct a quick line, or read a small piece of code.

It is not something you want to load the whole IDE and all of its features for. Your time is precious, so people often use a basic third-party lightweight editor. At the start of this year, IntelliJ IDEA began providing the ability to open files in a lightweight version of the editor that loads very quickly and has a limited set of basic features, which is perfect for these quick changes.

Code with me and Projector

These are new solutions that work with IntelliJ IDEA and our other IntelliJ-based IDEs. Basically, they let you set up an IDE instance on a server with high hardware resources, and access it remotely via different protocols. All the heavy operations are then run remotely on a faster machine so you can get results sooner.

With Projector, you can access the server using a browser. The web client is implemented using HTML5 Canvas API. In March 2021, we announced Projector 1.0. Let us know if you are interested – we’ll be happy to assist you with getting started. You can contact us anytime through YouTrack and Twitter.

With Code With Me, you install a lightweight version of IntelliJ IDEA on your machine and set up a connection with the host. Code With Me was initially intended for collaborative development purposes, where you share your IDE session with your colleagues. Later we found that using it in a remote development scenario with a server-based IDE instance works very well, too. To learn about how it works in more detail, please visit the product’s official page.

Tips for improving IDE performance

In addition to using these solutions and features, you can also modify your project and IDE configuration to make it work faster. Most of these tips are trade-offs between the smart capabilities of the IDE and the performance that you get, but they can really help you out if you are experiencing performance issues. 

IDE and Project configuration

1. Disable all unnecessary plugins

Open the IDE’s Preferences/Settings → Plugins menu and make sure that you don’t have any plugins installed that you don’t use. IDE slowness can be caused by many things. We have received reports where the logs show plugin operations interfering with the code highlighting process, but a user may not realize this is the cause from the UI behavior.

2. Increase memory for IntelliJ IDEA

By default, the IDE requests a maximum of 2048 MB. You can change this option in Help → Change memory settings. According to the feedback from some of our users and colleagues with big projects, changing the option to 16GB significantly improves IDE performance.

3. Exclude folders and Unload modules

You can reduce a scope that the IDE has to index and use for searching operations like Find Usages or Code Completion. You can consider excluding folders that contain generated files, logs, project documentation, etc. For unloading – modules that you don’t need and you’ll not use. You can find more details in the IntelliJ IDEA documentation.

4. Disable on-the-fly import management

The IDE almost completely automates import management in source files. Although the majority of capabilities don’t use many resources, there are a couple that can make the IDE performance slower. The settings can be found at Preferences → Editor → General → Auto Import → Kotlin:

  • Add unambiguous imports on the fly
  • Optimize imports on the fly

The first option substitutes the explicit context actions call (Alt+Enter) on an Unresolved reference if there is only one available import. The second performs import list optimization after all possible relevant actions, instead of one explicit Optimize imports action call. We intentionally extracted these options because any additional value that they bring may be offset by performance degradation.

5. Pause inspections check

If you’re in the process of writing new code and warnings are not that important to you, you can change the highlighting level to Syntax. This way, code inspections won’t run and consume IDE resources. To make this change, hover over the Inspections widget and use the link at the bottom of the popup to change the highlighting level to Syntax or None.

Code Style

These suggestions are not a call to action, but rather things to consider if you and your teammates are okay with the established style in your project. Kotlin provides features like Type inference and Extension functions, but sometimes we see how those features, when overused, make code harder to read and also affect IDE performance by overloading the code analysis. 

Explicit type annotations for public methods

Public methods and properties represent a so-called “public API,” which is a form of abstraction. Forcing API users to investigate the inner workings of public methods (or to guess the result type) is a sure way to break encapsulation and degrade code quality. Explicit type annotation makes public API well-marked and easy to use.

What’s also important is that explicit result types can greatly improve code editing performance. Unlike the compiler, an IDE has to re-infer types on each code update. To optimize the workload, IntelliJ IDEA tries its best to cache and preserve as much type information as possible. However, as the result type of a method with omitted type annotation can change, even on “external” update, IntelliJ IDEA has to re-process the whole content of such a method on many kinds of file change. In addition to that, type annotations on public methods increase the speed of incremental compilation by minimizing the amount of recompiled code.

File size

There’s no doubt that there are cases of long files when their structure is reasonable. At the same time, we know “code smells” like the “Long method” and “God class”. We also know about the Single-responsibility principle, etc.

Kotlin spec. discusses it:

Placing multiple declarations (classes, top-level functions, or properties) in the same Kotlin source file is encouraged as long as these declarations are closely related to each other semantically, and the file size remains reasonable (does not exceed a few hundred lines).

This is very important in terms of IDE performance. File is one of the scopes that the IDE considers when re-analyzing after every code modification. Optimization in code analysis algorithms helps, but it does not save us from performance degradation as a result of increasing file size.

How to report problems

We do lots of tests inside (read more in this post) to figure out things that might not work smoothly, but sometimes issues can only be found in specific situations encountered only in real-life projects. If you face something that doesn’t work the way you expected, the best way to get it fixed or changed is to tell us about it. 

Reporting issues to YouTrack

Our public bug-tracker is the place to tell us about any performance problems you experience. The tickets are seen by the Kotlin team, who can properly process them. YouTrack helps us keep track of useful information – logs, comments, changes story, and links to other tickets. All of this helps us solve the problems.

How the process works:

  1. Report a ticket in our issue tracker
  2. The Kotlin team analyzes all incoming tickets and classifies them according to the type of performance problem and the possible cause. Our technical support engineers may request some additional info, or add some findings they get from the information you provided to the comments.
  3. Kotlin and IntelliJ IDEA developers review the information submitted to the ticket. 
  4. When enough data has been collected, and after some research, we usually identify a root cause for the slow behavior and fix it.
  5. After we are sure that we have fixed this problem, we close these tickets. Sometimes, there can be several problems behind a specific slow behavior that you report. Or, we can not be sure that the problem has gone completely after the fix. So we keep issues open until we have fixed all the problems that were discovered as a consequence, even after the main cause was fixed.

Uncertainties in a real status of opened issues usually appear when we rewrite some subsystems (e.g. Code Highlighting API). It’s almost impossible for us to know for sure how your case will behave under these new circumstances. We strive to have the information in tickets relevant and up to date (for the latest versions of the plugin and IntelliJ Platform). Here we ask you to retest the described behavior and attach new logs if the reported issue is still open after some time.

The best way is to create an issue in YouTrack is describing the problem and attaching the relevant logs so we can start investigating it

  1. Describe exactly how the problem arises, what you were doing, and where in the file it occurred.
  2. Provide diagnostic information about the state of the CPU and memory. It can be easily obtained following the instructions here.
  3. Attach the logs from IntelliJ IDEA, made with the action [Help → Collect Logs and Diagnostics data]
  4. Add information about the platform, plugins, and other versions from About IntelliJ IDEA. Hover the mouse over the list of versions and click on the Copy button that pops up. You may remove information about your License and Subscription.
  5. If the project is publicly available, please attach it or share a link to where we can download it.
  6. Otherwise, please describe some traits of the project: how many modules, files, or (Kotlin or Java) classes the project contains to give us a rough understanding of it. Of course, we don’t need an exact number but want to understand the magnitude.
  7. If the problem arises during the editing of a file, specify the approximate file size or how many classes of top-level function it contains. Or just let us know that there’s nothing unusual.

The list turned out to be quite long, and we don’t require you to report all of this – consider it a recommendation. Each of these pieces of information can help us to catch the real reason for the performance slowdown.

Also, to help us get more real usage data, you can opt-in to send us anonymous statistics. It’s easy, and we won’t get any details about your code or project as the statistics are anonymous and don’t contain sensitive data.

Share my anonymous data

Related links

How we measure Kotlin IDE performance
Kotlin 1.5.0 – the First Big Release of 2021
Kotlin Plugin 2021.1 Released: Improved IDE Performance and Better Support for Refactorings
Nine Highlights from the Kotlin Roadmap
Coding conventions

Continue ReadingSimple Steps for Improving Your IDE Performance

Kotlin 1.5 Event Materials and the 10 Most Interesting Questions from the Q&A

The recordings of our 1.5.0 release celebration event are out and available for you on the Kotlin YouTube Channel. 🔔 Subscribe to the channel updates and stay up to date with the Kotlin future events!

Recordings and slides 

Our amazing hosts – Svetlana Isakova and Sebastien Aigner – gave a talk about the 1.5.0 updates, and Roman Elizarov presented on the future of Kotlin. After that, members of the JetBrains Kotlin team and guests from Google and VMware answered questions from the attendees. 

Check out the recordings:

And slides of the talks

If you would like to learn more about the Kotlin 1.5.0 release, please check out these materials:

Q&A and “Ask Me Anything” session with the Kotlin team

We were thrilled to receive so many questions from you – 630 questions before the event and 150 more during the live stream! Thank you everyone who asked questions! It’s encouraging to see so much interest and engagement from the community!   

A one-hour Q&A session wasn’t enough to cover all of them – that’s why a day after the event, we caught up during an Ask Me Anything session on Reddit. Please check out the Kotlin 1.5 Online Event thread. Join the r/Kotlin subreddit for news and notifications about future AMA sessions. 

The 10 most interesting questions from the event

We picked the 10 most interesting questions, and the winners will receive special Kotlin 1.5 T-shirts. The questions cover details about Kotlin updates, best practices, and important topics for future plans. You can check out the answers below and even jump into the discussions on Reddit! 

Question #1:
Do you think large monolith Kotlin projects will also get a performance boost in future versions?

“Of course! Our new front-end is doing exactly that – speeding up the compilation of a single module. So if you’re stuck with one big module and can’t use Gradle, for example, in parallel, then the new front-end should make you happy.” Proceed to the full discussion on Reddit.

Question #2:
What’s the team vision on compiler plugins in the long run?

“Current compiler plugin API mostly was created as ad-hoc solutions for the needs of kotlinx plugins (like kotlinx.serialization). We plan to properly design it when the new compiler becomes stable”. Read the full answer on Reddit 

Question #3:
Are there plans to support WebAssembly in Jetpack Compose for Web?

“It might be a bit early to say just yet, but I’d personally love to see it! If you have a particular reason why you’d be excited about the combination of WebAssembly and Compose, we’d also be interested to hear it! :)” Post your suggestions on Reddit.

Question #4:
Any news on the Kotlin Native garbage collector rewrite?

“While we’re on the topic, I’d like to clarify something. You might hear about Kotlin/Native’s new garbage collector, or a new memory manager, or a new memory model. It’s mostly all the same! The garbage collector is a part of the memory manager. Often these terms are used interchangeably.

Now to the news!
We’ve settled on starting with a simple stop-the-work, mark and sweep GC, and are considering evolving it later to a concurrent mark and sweep GC.” Read the full answer on Reddit and our recent blog post “Kotlin/Native Memory Management Update

Question #5:
With Java adopting many features that Kotlin has or had, how will Kotlin stand out against Java after JDK17 is released?

“The key features of Kotlin, like its null-safety, equal treatment of val/var, concise and modern design, extensibility, and DSL-friendliness are nowhere to be found in Java’s roadmap. These still make programming in Kotlin safe, fun, and enjoyable. This is even without taking into account that Kotlin evolves and never stands still.” Read more on Reddit

Question #6:
What is the ultimate goal of Kotlin? Is it something like ‘Write in one language, used in everywhere’? (I thought this because of Kotlin Multiplatform, KMM, Compose for Web, Desktop, etc.)

“In short, yes. We are striving to create a general-purpose application development language, well-suited for all kinds of applications in different domains and working on different platforms.” Answer on Reddit

Question #7:
Have you already tried Project Loom in Coroutines implementation?

“We keep checking it, but we don’t know if it will be in the final API. At the moment we’re waiting for Loom to graduate at least to the preview stage.” Full answer from the online event

Question #8:
As a tech lead with experience of leading a real world project with KMM, it’s harder to get iOS developers to accept this stack. This would be easier with AppCode integration, so you have such plans to support the iOS developer experience further?

“The experience for iOS developers was always a pretty noticeable pain point there, but recently we’ve seen that it’s getting more and more traction (we’d like to think that it’s because we’re removing other, more apparent roadblocks, but who knows :D)” Read more on Reddit

Question #9:
Are (or will be) there any tools to access compiler and analyzer api for external usage? The only way now is using Kotlin compiler source code itself.

“Yes. I think at some point we will create a separate additional jar file with the public compiler API.” Read the full discussion on Reddit

Question #10:
Why main() isn’t use in Android development?

“Even though Android shares a lot of the underlying components (such as the Linux kernel) and concepts (such as processes) with traditional operating systems, it has a very different model for running user applications. This is because it operates on a different set of constraints, such as limited battery capacity, and expectations, especially when it comes to security and privacy of user data. This leads to a stronger sandbox model and affects how users interact with their devices throughout the day.” Read more on Reddit

More helpful links and materials 

Find more questions and discussions under the Kotlin Team AMA #3 post. Thanks again to everyone for your active participation in the Q&A! 

Check out other release materials:

Continue ReadingKotlin 1.5 Event Materials and the 10 Most Interesting Questions from the Q&A

Kotlin IDE Performance

The performance of your tools, and especially your IDE, has a significant impact on your development experience. For a long time, performance was a pain point for Kotlin developers, especially those who worked with large codebases or with complex code structures. This is why for the past year we’ve been focusing on improving this experience for you. Our goal is to make sure working with our tools is as smooth as possible. We’ve already done a lot of work on that front, but we’re not going to stop here!

If you have ever experienced the blight of a slow IDE, or if you get a kick out of software performance, you’ll like what we’ve prepared for you below. We’ll cover:

Our recent versions have fixed a lot of performance issues. If you’ve been reluctant to upgrade because of concerns about breaking changes, we think you’ll be excited by the improvements we’ve made. 

Measuring IDE performance

We use a variety of approaches to finding problems with IDE performance. Our primary resources for measuring IDE performance are synthetic and real-life benchmarks, anonymous statistics, and your feedback.

Synthetic benchmarks

We run benchmark tests for code completion and highlighting for internal components on basic code snippets on every build. Each test is performed several times, and the geometric mean of these runs is reported. Each suite of benchmark tests includes around 1000 tests. We run them in both before- and after- warm-up modes, roughly ~20 times in each (40 times total), and then we take the mean and geometric mean for each mode.

Such benchmarks help us catch regressions and estimate the performance improvements that result from changes in the algorithms in particular subsystems. A degradation in IDE performance can sometimes be caused by a change in another subsystem. Like in this case, where a change in the compiler’s frontend cache affected the speed of code highlighting. We discovered it, reported the problem to the compiler team, and they fixed it. 

It is hard to use these representations to draw conclusions about the actual user experience because users just see the end result of the many integrated subsystems. Therefore we run a second level of tests.

Real project benchmarks

We use internal JetBrains projects – those using Kotlin – to test IDE performance. For instance, we use a JetBrains project written in Kotlin for our code highlighting tests. We categorize the files by complexity, from very simple ones to complex ones that contain many declarations and require a lot of calculations for type inference, semantic analysis, etc. The files have different highlighting times, which allows us to see how performance is affected in different situations.

First-time file highlighting time in files of different complexity

Additionally, we set up a highlighting test that runs over project files and presents a more general picture of the state of the code highlighting in the project for particular builds. The test takes 10% of the largest files, 10% of the smallest ones, and 10% of the files in the middle. It doesn’t make much sense to run it for every build, but it can be useful to see how the results of this test change from release to release. For instance, this is analytics of highlighting tests over Space project – the integrated team environment that was recently released by JetBrains.

This image has an empty alt attribute; its file name is IDE-perfomance-infographic_4-02.png
Highlighting time distributed over percentiles. E.g., 99% of the project.kt files were highlighted faster than 8.06 seconds.

We actually have a question for you regarding this. We are considering making these benchmarks public, but we aren’t sure whether you would actually get any benefit from them, and we don’t know what use cases they could help with. If this is something you would be interested in, please share your thoughts with us in this issue.

Share my thoughts on benchmarks

Anonymous statistics

The most accurate picture we can get of how our product behaves in user workplaces comes from anonymous telemetry data, which we gather from those who agree to share it. This data allows us to get a better understanding of your experience of IDE responsiveness. We log code completion and indexing times. And even though such data doesn’t tell us anything about code, we can understand how a feature’s performance changes relative to the numbers from the same user in the same project.

Time of Code Completion pop-up appearance

You could help us collect more real usage data by opting-in to send us anonymous statistics from your project. We won’t get any details about your code or project, as the statistics are anonymous and don’t contain any sensitive data.

Share my anonymous statistics

Your feedback

And of course, we process all the tickets you report in YouTrack. By tracking their frequency, we can get a better understanding of what areas we should focus on. For instance, this is how we found a performance regression (KT-43554) in IntelliJ IDEA 2020.2.3. The call sequence was fixed and delivered in the next release.

So if you find any issues with the release, please don’t hesitate to share it with us in YouTrack.

Improvements in the latest releases

When preparing this blog post, the latest release versions were IntelliJ IDEA 2021.1 and Kotlin 1.5.0.

Reworked platform, plugin, and compiler API for code highlighting

We’ve made a bunch of improvements to the code highlighting system to make it faster.

For “non-red code”, the new highlighting annotation API in the IntelliJ Platform lets us see the results as soon as we receive them, instead of receiving them all at once. This task had two iterations, and each brought us visible improvements to code highlighting speed. The related YouTrack tickets are KT-36712 and a part of KT-37702.

The update is a bit different for “red code”. Here we do on-the-fly compiler analysis diagnostics reporting. We have reworked the IDE plugin, including by adjusting compiler interaction so that the compiler reports all diagnostics (like Unresolved Reference or Type Mismatch) as soon as it detects them. Previously, such diagnostics came bundled together in a related ticket: KT-37702.

Also, all highlighting jobs (basic syntax, semantic analysis, code inspections) were previously being run simultaneously. But now, we’ve defined an order, putting syntax and semantic analysis before the other jobs; the related ticket is KT-37553.

Code analysis optimizations for code highlighting

We perform incremental code analysis where possible and where it makes sense. Also, we reduce the need to re-analyze jobs by optimizing the caches and analysis algorithms.

One of the biggest improvements was in the optimization of resolve calls – heavy operations that require compiler involvement. Before involving the compiler, we do all the possible code analysis within the IDE, like testing whether the code returns null and other lightweight checks, like here or here. Also, IntelliJ IDEA now displays the “Run test” gutter icons before semantic analysis starts. We managed to do this by introducing some heuristics – the related ticket is KTIJ-408. And here’s an example of how it works on a complex file with cold IDE caches.

Gradle Scripting support

To improve Gradle Scripting support, we concentrated on the interaction of three subsystems: IntelliJ IDEA, Gradle, and Kotlin. They all need to get all the data as fast as possible, but some operations are heavy and take time. We investigated such cases in order to minimize their effect on the user experience. Here’s what we’ve done:

  • Rewrote the API, making it possible to distinguish operations in terms of their heaviness
  • Changed the way IntelliJ IDEA and Kotlin get script configurations from Gradle
  • Added more caches for heavy operations

You can find more details about the Gradle Scripting support in the dedicated blog post.

Polishing code completion algorithms 

Code completion is a complex subsystem that includes searches, indexes, and prioritization algorithms. Code completion slowdowns are usually caused by high CPU/Memory consumption in another system. But sometimes we can find some ineffectiveness in code completion algorithms themselves and fix them, as we did for this issue and this issue. We make sure to keep track of how our algorithms perform and tune them whenever we notice some slowness.

IDE responsiveness

There are three main causes of IDE hangs:

  • Heavy operations on UI threads.
  • Inappropriate usage of “ReadAction” and “WriteAction,” the main actors in the IDE dynamics and memory model.
  • Deadlocks – the problem of multithreading applications.

The most effective way to deal with the responsiveness problem is to collect diagnostics information from YouTrack reports, analyze it, and identify the problematic areas. Responsiveness issues can occur anywhere in the IDE: import management, formatting, copy/paste actions, code analysis, project configuration, etc. We have been looking into these for some time and are always trying to improve responsiveness. The list of fixed issues can be found in our YouTrack.

Share my anonymous data

CPU/Memory consumption

To improve memory consumption, we profile problematic cases, detect leakages, and fix them. There is nothing surprising about this approach, and we do the same for a high CPU consumption. With the help of the diagnostics information you provide, we are able to detect places that cause infinite calculations or move many heavy processes into the most elevated priority pool.

To provide diagnostics information, you can follow these simple steps:

1. Create a ticket, which should contain diagnostic information about the state of the CPU or memory. It can be easily obtained from the description here.

2. Attach the logs from IntelliJ IDEA [Help → Collect Logs and Diagnostics data])

Our plans for the future

We’ve made some progress in code highlighting, completion, and IDE responsiveness, but what about other areas? 

We are dedicated to building on what we have done and want to share with you our upcoming plans for improving performance further.

Search and Navigation

While investigating the possible benefits of deeper integration between the build tools and IntelliJ IDEA, we found we could reuse information from the build in the IDE’s search system instead of gathering it again just for the IDE. We expect to significantly speed up Find Usages, Navigation, and Search references by building a new index. This will be helpful for projects with large code bases, where regathering information is a notable issue. 

Indexes rework

Updates to search and navigation are not the only improvements we’ll be making to indexing. We’re also planning to review all the indexes that the Kotlin plugin currently uses. We want to fix any outdated or improper API usages of the IntelliJ Platform index system to remove instances of unnecessarily repeating calculations. This refactoring should reduce IDE re-indexing, as well as failures of fast code analysis and search operations.

New compiler frontend

This is the longest running task, but it promises to be a game-changer in areas related to compiler execution time. We noticeably increased code processing speed. Also, we designed a better compiler API that allows IntelliJ IDEA to make more specific requests to the compiler and reduce the amount of work required for a particular job.

Shortening the feedback loop

Some big IT companies have a special department that is dedicated to providing the best development experience for their colleagues. Among other things, these departments measure how the IDE behaves with their project on new versions of plugins, the IntelliJ Platform, tooling, or libraries. 

We see mutual benefit in cooperating with these departments to get detailed technical analysis and to deliver fixes more quickly. In return we will be able to improve performance in specific areas. These fixes will be beneficial to most of our users.

To this end, we are working on establishing a process with IT departments. If you are interested, please send an email at anton.yalyshev@jetbrains.com.

Summary

If you’ve been trying to decide whether to update to new versions of Kotlin or the Kotlin Plugin, our official position is yes! It totally makes sense to upgrade if you’ve been experiencing some performance issues, as there’s a good chance the recent changes have addressed the problem. 

How to update to the latest version of the Kotlin Plugin in IntelliJ IDEA

IntelliJ IDEA will give you the option to automatically update to the new release once it is out. Or you can download the newest version and it will already come bundled with the latest Kotlin plugin.

Related links:

Continue ReadingKotlin IDE Performance

End of content

No more pages to load