Kotlin 1.3.70 Released

Posted on by

Today we’re happy to present to you the latest version of Kotlin – 1.3.70.

This incremental release doesn’t provide any major new features. However, we’ve tried our best to improve the existing functionality, fix issues, and even add experimental things for you to try. Here are the highlights of Kotlin 1.3.70:

New functions and classes for Kotlin collections in the standard library.

Various improvements in the IntelliJ Kotlin plugin: improved *.gradle.kts support, testing, debugging, completion, and so on.

support, testing, debugging, completion, and so on. The Kotlin/JVM compiler now generates type annotations in the bytecode for Java 8 and later targets.

Bundle optimizations, npm dependency declarations, and long-awaited new docs for Kotlin/JS.

Faster compilation and debugging for Kotlin/Native.

Improved support for scripting in the IDE and command-line tools.

You can find the complete list of changes in the change log. As always, we’d like to thank our external contributors.

Now let’s dive into the details!

Standard library changes

Note that all new functions are added to the standard library in the experimental state.

Extending StringBuilder in the common library

StringBuilder was already present in the common standard library, in the kotlin.text package. However, many important members were missing or were available only on the JVM. Now, all the JVM StringBuilder functionality was added to the common expect class with the corresponding implementations on different platforms. This means you can effectively use StringBuilder from common code as all the necessary members are there.

Working with KClass

Some basic useful members on KClass no longer require a kotlin-reflect dependency on the JVM:

import kotlin.reflect.cast @OptIn(ExperimentalStdlibApi::class) fun main() { val kClass = String::class println(kClass.simpleName) // String println(kClass.qualifiedName) // kotlin.String println(kClass.isInstance("abc")) // true println(kClass.isInstance(10)) // false println(kClass.cast("abc")) // abc }

Before, you needed to provide a Kotlin reflection implementation at runtime to make it work. Now you can use these simple members without additional dependencies.

Renaming of experimental annotations (@Experimental and @UseExperimental)

As you may know, Kotlin has a built-in mechanism for using experimental features. It includes annotations from the standard library, which mark declarations that are either experimental themselves or use other experimental declarations. In previous versions, these were @UseExperimental and @Experimental .

We’ve decided to widen the scope of this mechanism, because something being in the experimental state is not the only reason to require consent for using APIs. For example, an API can be internal or have some restrictions. We have renamed the annotations to reflect this: the names @OptIn and @RequiresOptIn have replaced @UseExperimental and @Experimental , accordingly. The compiler argument -Xuse-experimental has been renamed to -Xopt-in . As for -Xexperimental , we’re dropping it because how rarely it is used and how much it increases complexity. The old declarations @Experimental and @UseExperimental are still supported in 1.3.70, but will be dropped in 1.4.

Renaming of experimental time measurement API

Another renaming we’ve made concerns the Duration and Time measurement API. Clock and ClockMark have been renamed to TimeSource and TimeMark , accordingly. The previous names are kept as deprecated type aliases for now.

Double-ended queue implementation: ArrayDeque

We’re happy to add the implementation of the double-ended queue, the kotlin.collections.ArrayDeque class, to the Kotlin standard library! The community has been asking for it for some time. Even though you could use the java.util.ArrayDeque class from the Java standard library, there was no common implementation you could use for Kotlin/JS, Kotlin/Native and, most importantly, in common code. Now such an implementation is available, albeit in the experimental state.

A double-ended queue allows you to add/remove elements both to/from the beginning and the end of the queue in amortized constant time:

@OptIn(ExperimentalStdlibApi::class) fun main() { val deque = ArrayDeque(listOf(1, 2, 3)) deque.addFirst(0) deque.addLast(4) println(deque) // [0, 1, 2, 3, 4] println(deque.first()) // 0 println(deque.last()) // 4 deque.removeFirst() deque.removeLast() println(deque) // [1, 2, 3] }

You can use a double-ended queue by default when you need a queue or a stack in your code.

The kotlin.collections.ArrayDeque implementation uses a resizable array underneath: it stores the contents in a circular buffer, an Array , and resizes this Array only when it becomes full.

Conceptually, the implementation of ArrayDeque is very similar to that of java.util.ArrayDeque . Note, however, that it’s a different implementation and this new implementation will be used when you use this class for Kotlin/JVM. This differs from how it works with other collections: when you create an ArrayList and compile it to JVM, the java.util.ArrayList class is used under the hood. Unlike Java’s ArrayDeque , which implements only the Collection interface, Kotlin’s ArrayDeque implements MutableList . This means you can access all the elements by index, which is not possible in Java’s ArrayDeque .

We’re releasing the ArrayDeque class now in the experimental state, and are looking forward to your feedback!

Collection builders

Another important new functionality is builder functions for collections: buildList , buildSet , and buildMap . You can use such a builder function to conveniently manipulate a mutable collection during the creation phase and get a read-only collection as a result:

@OptIn(ExperimentalStdlibApi::class) fun main() { val needsZero = true val initial = listOf(2, 6, 41) val ints = buildList { // this: MutableList if (needsZero) { add(0) } initial.mapTo(this) { it + 1 } } println(ints) // [0, 3, 7, 42] }

These builder functions are implemented similarly to buildString . buildList takes a lambda with a receiver as an argument. An implicit “this” receiver inside the lambda has the type MutableList , while buildList returns a read-only List as a result.

It is often more readable and more effective in terms of performance to use the builder functions when you need to perform complicated manipulations, like using conditions, modifying several initial collections and merging the result, and so on. Note that these functions are also currently released in the experimental state.

reduceOrNull() and randomOrNull() counterparts

You know this convention in Kotlin: having a pair of functions, where the first one throws an exception if the operation isn’t possible, and the second one returns null , like string.toInt() and string.toIntOrNull() . Now we’ve added new randomOrNull() and reduceOrNull() counterpart functions, following the same convention:

@OptIn(ExperimentalStdlibApi::class) fun main() { val list = listOf(1, 2, 3) println(list.randomOrNull()) // 2 println(list.reduceOrNull { a, b -> a + b }) // 6 val emptyList = emptyList<Int>() println(emptyList.randomOrNull()) // null println(emptyList.reduceOrNull { a, b -> a + b }) // null }

If you use random() or reduce() , you’ll get an exception if the collection is empty.

scan() functions

We’re adding a new set of functions for working with lists and sequences. They represent the concept of “scanning”; similar functions are already present in different libraries and languages.

scan() is closely related to fold() . Both scan() and fold() apply the given binary operation to the sequence of values, but differ in that scan() returns the whole sequence of intermediate results, while fold() returns only the final result.

@OptIn(ExperimentalStdlibApi::class) fun main() { val ints = (1..4).asSequence() println(ints.fold(0) { acc, elem -> acc + elem }) // 10 val sequence = ints.scan(0) { acc, elem -> acc + elem } println(sequence.toList()) // [0, 1, 3, 6, 10] }

Like fold , scan takes an initial accumulator value:

If your result type is the same as an element type and you can use the first element as an initial value, then you can use the reduce() and scanReduce() functions:

Note that when you use scan() and scanReduce() on sequences, they return sequences as a result, which are lazy by nature. That means that the scanning process only starts when you ask data from the resulting sequence, specifically call a terminal operation (the one returning something other than another sequence), such as toList() or max() . When you use scan() and scanReduce() on lists, they return lists as a result.

Read this KEEP for more details.

IntelliJ IDEA support

This release includes several improvements for Kotlin support in IntelliJ IDEA. Let’s walk through the most interesting ones.

*.gradle.kts support

In 1.3.70, we worked to improved IntelliJ IDEA’s support for Gradle Kotlin DSL scripts ( *.gradle.kts files). As a result, the latest version of the Kotlin plugin demonstrates better performance in syntax highlighting, completion, search, and other aspects of working with Kotlin build scripts. A detailed review of improvements is provided in this blog post.

To enjoy all the changes and improvements, make sure to use IntelliJ IDEA 2019.2 or later with Gradle 6.0 or later.

Code completion

In 1.3.70, we’ve made noticeable improvements for Kotlin code completion in IntelliJ IDEA. Now, completion suggestions include functions declared in objects, including extension functions, object-level overrides, and even functions declared in nested objects.

We’ve also improved the machine learning model that sorts the completions list, and now, the most relevant options appear at the top.

New color schemes

To enable you to change the appearance of Kotlin code in the editor to your liking, we’ve added new customizable color schemes. In particular, now you can set your own color schemes for the suspend function calls and property declarations.



Debugging improvements

In previous versions, the Kotlin/Native debugger used to have a separate breakpoint type, confusing some users with an unclear choice like “which breakpoint type should I use here?”. Now the single breakpoint type Kotlin Line Breakpoint works for both JVM and Native targets.

Kotlin/JS and Kotlin/Native tests

Test results for Kotlin/JS and Kotlin/Native are now displayed right in the IntelliJ IDEA, as it has always been for Kotlin/JVM tests. Besides, we’ve fixed test filtering for Kotlin/JS, so you can run individual tests, and Kotlin/Native tests targeting the iOS Simulator can finally also be launched directly by pressing the “Play” button.

Moreover, in IntelliJ IDEA Ultimate, you can start the Kotlin/JS debugging by clicking an icon near the test declaration.

Alternatively, you can start debugging by manually choosing nodeRun , nodeTest , or browserTest task in the Gradle tool window. For browserRun , you can attach debugger after starting the development server using the Attach to Node.js/Chrome run configuration.

Other notable mentions

There are also other improvements in Kotlin support in IntelliJ IDEA. Here are some worth mentioning:

Improved performance of file analysis and copy-paste action.

New inspection on pointless unary operators. They may appear when splitting long expressions onto multiple lines:



Numerous formatting improvements, including formatting of call chains, line breaks, spacing, and comments.

Kotlin/JVM

Kotlin now can generate type annotations in the JVM bytecode (target version 1.8+), so that they become available at runtime. This feature has been requested by the community for some time, because it makes usage of some existing Java libraries much easier, and it gives more power to those authoring new libraries.

In the following example, the @Foo annotation on the String type can be emitted to the bytecode and then used by the library code:

@Target(AnnotationTarget.TYPE) annotation class Foo class A { fun foo(): @Foo String = "OK" }

To emit the type annotation in the bytecode, follow these steps:

Make sure that your declared annotation has a proper annotation target (Java’s ElementType.TYPE_USE or Kotlin’s AnnotationTarget.TYPE ) and retention ( AnnotationRetention.RUNTIME ).

or Kotlin’s ) and retention ( ). Compile both the annotation class declaration and the code using this annotation (class A in the example above) to JVM bytecode target version 1.8+. You can specify it with -jvm-target=1.8 compiler option.

compiler option. Compile the code using the annotation with the -Xemit-jvm-type-annotations compiler option.

Note that the type annotations from the standard library aren’t emitted in the bytecode for now because they are compiled with the target version 1.6.

So far, only the basic cases are supported:

Type annotations on method parameters, method return types and property types;

Invariant projections of type arguments, e.g. Smth<@Ann Foo> , Array<@Ann Foo> .

In the future, we’re planning to support generating type annotations for covariant and contravariant type projections and annotations on inner types (such as Smth<@Ann Outer.Inner> ). We believe that the supported cases should already cover most real-life scenarios, but if you need type annotations for more complicated cases, please let us know.

Kotlin/JS

With Kotlin 1.3.70, the JavaScript target receives some significant optimizations in terms of bundle size, and adds a few quality-of-life changes to the way dependencies, resources, and tests are handled. We’re also happy to announce our documentation overhaul for the target.

Bundle optimizations

Starting with Kotlin 1.3.70, DCE (Dead Code Elimination) is now readily available through the org.jetbrains.kotlin.js Gradle plugin and does not need to be manually added. It receives a set of new tasks that you can use to control the optimization and execution of your JS project.

During development, use browserDevelopmentRun to start a non-optimized run of your application with the bundled development server, and use browserDevelopmentWebpack to create a non-optimized bundle of your application in the build/distributions folder. If you choose not to run any optimizations, your build times will be faster, but compiled programs will have larger file sizes.

When preparing for production, use browserProductionRun to start an optimized run of your application, and use browserProductionWebpack to generate an optimized bundle that is fit for deployment in production. Production builds take a little longer to compile, but end up with a significantly better-optimized bundle than their development counterparts:

Project Development Build (gzip) Production Build (gzip) “Hello, World” 963 KiB 14 KiB Kotlin/React Example (“CodeQuiz”) 4.1 MiB 345 KiB

While the previous Gradle tasks browserRun (now an alias for browserDevelopmentRun ) and browserWebpack (now an alias for browserProductionWebpack ) are currently still available, you should replace them with their newly added equivalents in any of your build scripts.

These Gradle tasks are available for projects using the multiplatform or kotlin.js Gradle plugins. For kotlin.js , you can also use the shorter task aliases run for browserDevelopmentRun and build for browserProductionWebpack .

If you’d like to learn more about Dead Code Elimination in Kotlin/JS and how to configure it in a fine-grained manner, check out the documentation.

Automatic copying of resources

Build tasks that create bundles in the distributions folder (like browserProductionWebpack ) now also copy all assets from the resources folder, so it is no longer required to manually copy images, stylesheets, and others to get a deployable set of artifacts.

Smoother support for npm dependency declarations

When using the Kotlin/JS Gradle plugin, it is now possible to declare npm dependencies inside the top-level dependencies block, instead of having to manually add a main source set. This means that the following snippet is now valid:

dependencies { implementation(npm("react", "16.12.0")) }

Experimental test debugging through Gradle

It is now possible to debug tests from within IntelliJ IDEA directly for the Kotlin/JS browser target. To debug a failing test, set a breakpoint in the IDE, and then either start the check Gradle task in debug mode or use the gutter icons to debug a set of tests. Gradle will execute the test task for the platform, and the debugger will stop at a synthetic breakpoint. It will hit the breakpoint as soon as Gradle shows the browserTest task in the console:

> Task :browserTest

At this point, switch to the :browserTest session in the debugger, and select the tab labelled “Debugger”. Click the “Resume Program” button to begin executing your tests:

Currently, this process is necessary to keep the debugger in IntelliJ IDEA in sync with the JS engine. We’re aware that this number of steps to start a debugging session is cumbersome, so keep a lookout in future versions for an even smoother debugging experience!

We also plan to provide the same experience for debugging Kotlin code that targets Node.js after resolving some remaining issues.

Improved documentation

With Kotlin 1.3.70, we have made some long-awaited changes to the documentation and tutorials for the JavaScript target, which hopefully makes it easier for people to get started with Kotlin/JS. We have refreshed the documentation and removed outdated materials. We’ve also added a new series of short-form tutorials to the website, which outline typical tasks that you might encounter when working with Kotlin/JS. They can help you get started with the Kotlin/JS Gradle plugin, and they cover typical tasks when building applications for the JavaScript target. The hands-on course “Building Web Applications with React and Kotlin/JS” has also been updated and can now teach you how to use the Kotlin/JS plugin for Gradle.

We hope that these changes will make it easier for you to get started and work with Kotlin/JS, especially if you want to try it for frontend development. We are committed to constantly improving the documentation and reference materials that help you learn Kotlin/JS. So, if you are still missing any essentials in the documentation or how-to’s, please let us know in the comments.

Kotlin/Native

Performance optimizations

In 1.3.70, we’re ready to show the first results of our work on optimizing the overall performance of the Kotlin/Native development process. Compile-time performance was one of the known weak points in Kotlin/Native development, so we’ve got two new features to decrease compilation times:

Now the Kotlin/Native compiler runs directly from the Gradle daemon, so no time is spent on starting a new process and loading the compiler into it on each compilation.

In the debug mode, the compiler caches project dependencies. Now the first compilation takes a bit longer, but the subsequent ones complete faster. Note that currently, this works only for iOS simulator (iosX64) and macOS (macosX64); we’re still working on this.

We have not forgotten about runtime and debug performance improvements either. In 1.3.70, we’ve cut some time spent on object allocation: now more objects are allocated on a stack (which is faster than heap allocation), and some singleton objects are created at the compile time. The debug process has become faster as well.

Support for multiple Kotlin frameworks in a single application

Previously, there was a known issue that an application could not use more than one dynamic Kotlin/Native framework because Obj-C classes defined in runtime were conflicting, coming from different instances of runtime. We’ve fixed this in 1.3.70, so now an application can use multiple Kotlin/Native frameworks. Any common dependencies are present in the frameworks under different Swift modules (and with different Obj-C prefixes).

Support for vector types

Starting with 1.3.70, Kotlin/Native supports SIMD types. This gives you many more third-party APIs available for using from Kotlin/Native, such as the popular Apple frameworks Accelerate and SpriteKit.

Scripting

Version 1.3.70 presents a set of improvements that provide a better experience of using Kotlin scripts with IntelliJ IDEA and Kotlin command-line tools.

Additionally, to help you become familiar with Kotlin scripting, we’ve prepared a project with examples. It contains examples of the standard scripts ( *.main.kts ) and examples of the scripting API usage and custom script definitions. Please try it and share your feedback in our issue tracker.

kotlin-main-kts support in compiler and IDE

In Kotlin 1.3, we introduced the kotlin-main-kts artifact which simplifies the creation and usage of the basic utility scripts. Now it is loaded by default by the Kotlin compiler and IntelliJ plugin, so that *.main.kts scripts are supported out of the box. In particular, now you can use such scripts without adding kotlin-main-kts.jar to the classpath.

In IntelliJ IDEA, this gives you highlighting and navigation, including resolve into dynamic dependencies, for *.main.kts files, even outside the source directories.

We’ve also improved the performance of the compiled *.main.kts scripts execution by enabling caching for them to make subsequent runs significantly faster.

Command-line tools

We’ve also extended the support for Kotlin scripting in the command line tools.

The kotlin runner distributed with the Kotlin command-line compiler now supports script execution. To run a script with kotlin , just pass the script file name to it:

kotlin myscript.main.kts

Such a call will run the script in exactly the same way as if you called the compiler with -script option. kotlin recognizes .main.kts scripts out of the box.

The kotlin runner can also be used for expression evaluation. To evaluate an expression, pass it as a value of the – e/-expression option:

kotlin -e "1 + 3 + 70"

You’ll get the result right away.

The same functionality is added to the Kotlin command-line compiler but with a different option name: -Xexpression :

kotlinc -Xexpression="1 + 3 + 70"

How to update

As always, you can try Kotlin online at play.kotl.in.

In Gradle and Maven : Use 1.3.70 as the version for the compiler and the standard library. See the docs for Gradle and for Maven.

: Use as the version for the compiler and the standard library. See the docs for Gradle and for Maven. In IntelliJ IDEA and Android Studio : Update the Kotlin plugin to version 1.3.70. Use Tools | Kotlin | Configure Kotlin Plugin Updates and click the “Check again” button.

and : Update the Kotlin plugin to version 1.3.70. Use Tools | Kotlin | Configure Kotlin Plugin Updates and click the “Check again” button. In Eclipse : Install the plugin using the Marketplace.

: Install the plugin using the Marketplace. The command-line compiler can be downloaded from the Github release page.

If you run into any problems with the new release, you’re welcome to ask for help on the forums on Slack (get an invite here), or to report issues in our issue tracker.

Let’s Kotlin!

External contributors

We want to thank Fleshgrinder for his work on adding support for builder functions for collections, and adellibovi for adding reduceOrNull() functions.

Thanks also to all of our external contributors whose pull requests were included in this release: