A major release just around the corner — meet Kotlin 1.3-M2

Moving full steam towards Kotlin 1.3, we’re happy to announce the second milestone release, Kotlin 1.3-M2, which unveils new features and improves the stability of those already announced. Some highlights:

  • Contracts improve smart-casts and other compile-time analyses
  • New Standard Library functions for unsigned types and collections
  • Reflection for coroutines
  • A migration layer to aid migration onto new coroutines being graduated in 1.3
  • Numerous bugfixes related to inline classes

We’d like to thank our external contributors whose commits were included in this release: Leonardo Colman Lopes and Pap Lőrinc.

The complete list of changes in this release can be found in the changelog.
We appreciate your feedback regarding the new features or any problems that you may run into with this release.

Contracts

In 1.3-M2 we introduce contracts, a new experimental addition to the Kotlin type system. Let’s consider a motivating example:

Here, require() is a function defined in the Standard Library that throws an exception when its argument is false (similar to assert(), but doesn’t depend on the -ea flag). Thus, if the println() gets executed, then x has to be a String, and a smart cast would be very logical in this line. The problem is: the compiler normally only looks at function signatures, so it knows nothing about how require() is implemented, and thus can’t infer that x is now certainly an instance of String.

With contracts, a function can tell the compiler things like “I affect smart casts this way” or “I execute this lambda immediately and exactly once”. Contracts enrich the type information available through the function signature (“I take a nullable list of T and return a Boolean”) with additional meanings useful at the call site, e.g. “I only return false for non-null lists”.

Kotlin standard library already comes with contracts added to several functions. They work regardless of the experimental flags.

Currently, the Kotlin compiler uses contracts for two types of improvements:

  • Making smart casts even smarter:

Applies for functions: check, checkNotNull, require, requireNotNull, kotlin.test.assertTrue, kotlin.test.assertFalse, kotlin.test.assertNotNull, isNullOrEmpty, isNullOrBlank

  • Analyzing variable initialization more accurately:

Applies for functions: run, let, with, apply, also, takeIf, takeUnless, synchronized

While the contracts of the standard functions listed above are stable, the declaration site syntax and APIs for defining contracts may be changed in the future. To specify contracts for your own functions, you have to opt into the experimental mode by using the @ExperimentalContracts annotation:

Note that the contracts are not yet verified at the declaration site, so it’s your responsibility to provide accurate information there. See this KEEP for the syntax, as well as for compatibility guarantees and general design overview.

Coroutines update

Kotlin 1.3 graduates coroutines to stable changing several things in the APIs and ABIs based on the feedback we gathered during the experimental period. In the previous milestone, 1.3-M1, there was no migration support bridging the old experimental coroutines with new stable ones. Now, in 1.3-M2 we provide such support: you can simply call old suspend functions.

Also, Kotlin reflection now supports introspection for suspend functions:

  • KCallable.isSuspend
  • KCallable.callSuspend
  • KCallable.callSuspendBy

The latter two allow for calling suspend functions via reflection, so they are themselves marked suspend.

Standard Library

Unsigned types

The library support has been improved for the unsigned integer types. Let’s walk through what has been introduced to make the unsigned types and arrays of unsigned types feel more like first-class citizens:

  • Functions for converting unsigned integers to and from strings in an arbitrary base: UInt.toString(base), String.toUInt(base) etc.
  • Extension functions until, downTo, step and reversed to support more ways of creating ranges and progressions of unsigned integers.
  • The arrays of unsigned integers do not implement the structural equality contract of List, same as the arrays of signed integers. To make it easy comparing these arrays or showing their contents as string the contentEquals(other), contentHashCode(), contentToString() functions were introduced.
  • Secondary constructors of unsigned arrays to create a zero-initialized array of unsigned integers of a given size.
  • Extension functions to copy unsigned integer arrays and their subranges: copyOf(), copyOf(newSize), copyOfRange(fromIndex, toIndex).
  • Extensions for reinterpreting an array of unsigned integers as an array of signed ones, and vice versa: ByteArray.as/toUByteArray(), UByteArray.as/toByteArray() etc.
  • Extensions for converting a specialized array of unsigned integers to an object array: UIntArray.toTypedArray() which returns an Array<UInt>, etc

Copying elements between two existing arrays

The array.copyInto(targetArray, targetOffset, startIndex, endIndex) functions for the existing array types, including the unsigned arrays, make it easier to implement array-based containers in pure Kotlin.

associateWith

It is quite common situation when you have a list of keys and you want to build a map by associating each of these keys with some value. It was possible to do it before with the associate { it to getValue(it) } function, but now we’re introducing a more efficient and easy to explore alternative: keys.associateWith { getValue(it) }

ifEmpty and ifBlank functions

Collections, maps, object arrays, char sequences and sequences now have ifEmpty function, which allows to specify a substitute value that will be used instead of the receiver if it is empty.

Char sequences and strings in addition have ifBlank extension that does the same thing as ifEmpty, but checks for the string being all whitespace instead of empty.

Random API improvements

The Random class, introduced in the previous 1.3-M1 release, has received extensions for generating unsigned numbers and arrays of unsigned bytes, thanks to our contributor Leonardo Colman Lopes: these are nextUInt, nextULong and nextUBytes extensions:

Also we’re introducing a helpful extension for collections, arrays and even ranges to get a random element from among all of the elements of that collection/array/range. It’s called just random():

Sealed classes reflection

We’ve added a new API to kotlin-reflect that can be used to enumerate all direct subtypes of a sealed class, namely KClass.sealedSubclasses.

Inline classes

In 1.3-M2 we introduce automatic mangling for names of functions that use inline classes in their signatures. We do it to prevent platform signature clashes when there are several overloads which are different only in the inline type, but not in the carrier type:

Another reason for mangling is to forbid (accidental) usage from Java, which may be undesired as inline classes are a purely Kotlin concept.
Mangled names will have the form of foo-<some string based on the signature>

Another two improvements for inline classes in this release are:

  • the support for secondary constructors
  • automatically generated toString/equals/hashCode functions

Language changes

We’re deprecating the support for older source language versions through the -language-version flag with values 1.0 and 1.1 for kotlinc-1.3 and above. Note that this change only affects compilation of source code for old target versions; all binaries ever compiled with stable language versions will be supported by later versions of kotlinc. To compile your sources with Kotlin 1.0 and 1.1 you can use the corresponding old versions of kotlinc.

Also, certain new restrictions were added in this update aimed to ensure stable and well-defined behavior regarding some error-prone constructs:

  • Misleading labels on statements other than loops, inline lambdas, and inline anonymous functions, such as foo@ val bar = 1, had no real meaning or use case and are now prohibited (KT-22274)
  • Getter-targeted annotations on annotation constructor parameters, which were not represented in the binaries and thus never worked, are now written to the annotation class getter methods (KT-25287)
  • Unresolved reference errors that were missing in data class constructor’s @get: annotations are now properly reported (KT-19628)

Pre-release notes

Note that the backward compatibility guarantees do not cover pre-release versions: the features and API can change in subsequent releases. When we reach a final RC, all binaries produced by pre-release versions will be outlawed by the compiler, and you will be required to recompile everything that was compiled by 1.3‑Mx.
However, all the code compiled by 1.2.x and earlier releases will be perfectly fine then without recompilation.

How to Try It

In Maven/Gradle: Add http://dl.bintray.com/kotlin/kotlin-eap as a repository for the build script and your projects; use 1.3-M2 as the version number for the compiler plugin and the standard library.

In IntelliJ IDEA: Go to Tools → Kotlin → Configure Kotlin Plugin Updates, then select “Early Access Preview 1.3” in the Update channel drop-down list, and then click Check for updates.

The command-line compiler can be downloaded from the Github release page.

On try.kotlinlang.org: Use the drop-down list in the bottom right-hand corner to change the compiler version to 1.3‑M2.

Let’s Kotlin!

This entry was posted in EAP, Releases and tagged . Bookmark the permalink.

27 Responses to A major release just around the corner — meet Kotlin 1.3-M2

  1. Louis CAD says:

    Congrats for this great release!
    Contracts is my favorite new feature, will allow powerful “DSL” use cases.
    Thanks to all the contributors and designers for fixes and new features, kudos.

  2. cretz says:

    Mentioned on Kotlin forum, but inline classes with type parameters seem to be broken.

  3. I wonder why is UInt not spelled as Uint?
    The names toUInt() and uintArrayOf() look inconsistent.
    If UInt is spelled as UInt, uintArrayOf() should be spelled as uIntArrayOf(), which IMO looks ugly.

    So if we want to be consistent, we have the choice of:
    (1) UInt, toUInt(), uIntArrayOf()
    (2) Uint, toUint(), uintArrayOf().

    In my opinion (2) looks better.

    • Ilya Gorbunov says:

      There’s another option, one that we have chosen:

      3) UInt and uintArrayOf()

      It is consistent with Int and intArrayOf() in the following sense: if you want to go from a signed type to the corresponding unsigned one, you just add letter U or u in front of it and don’t bother with capitalization of other letters.

    • Sergei says:

      There is a library where naming you’ve provided is being used (github/kotlin-graphics/kotlin-unsigned). Maybe JetBrains don’t want naming intersections.

  4. Scott says:

    Are compiler plugins being added in 1.3? I thought I had read something around this several months ago.

  5. Benjamin Kuchcik says:

    Contract is a réal great feature !

  6. Piotr says:

    Good job guys! Thanks for improvementst :)

  7. Nitro says:

    That’s really cool !
    I wonder one thing about the unsigned type, do you plan to add some extensions to the ByteBuffer class ?

  8. Christian says:

    Does Kotlin already have inline classes? The announcement reads like this, but I’ve never seen them in the docs.

  9. Kiffin Gish says:

    This is exciting news, gonna try out all the neat new features right away!

  10. Solomon Sun says:

    Excellent jobs ! Many ergonomics work like smarter cast !
    I like design spirit in Scala 3 (https://www.slideshare.net/Odersky/preparing-for-scala-3) :
    consistency
    safety
    ergonomics
    performance

  11. vicboma1 says:

    +1

  12. Anton says:

    Are you plan to add some checks when unsigned types goes to java and used wrong?
    Maybe with some annotations on methods which return unsigned types.

  13. Tim van der Leeuw says:

    Contracts sound like a neat feature. Can this be applied in some way that you can filter null values from a collection, without having to perform unsafe casts on it?

    So that something like this becomes legal:

    I haven’t yet seen anything in the std library that does that and sometimes find it useful.

    • Ilya Gorbunov says:

      You can use filterNotNull function exactly for that.

      The contract of the filter function was considered in the contracts proposal (see Open questions), but we haven’t yet found the way to express that contract.

      • Tim van der Leeuw says:

        I was looking for that earlier when I needed that, didn’t find it. (That was with Kotlin 1.1 I believe). I was looking for it again yesterday before posting this – and again didn’t see that. I used the autocompletions of the IDE to check for available functions, configured for Kotlin 1.2.61 I believe.

        Weird that I couldn’t find such a function if it already existed.

        • Ilya Gorbunov says:

          Could you please share some details in our tracker: http://kotl.in/issue?

          • Tim van der Leeuw says:

            In my code, the generic parameter T is not a subtype of anything.
            In the declaration of filterNotNull, the type parameter T must be a subtype of Any.
            Therefore it did not show up in my list of completions, and when I typed it out explicitly, I got a clear error from the compiler.

            Is there any particular reason why this function out of all others is defined as
            <T: Any> Iterable<T?>.filterNotNull(): List<T>

            Instead of
            <T> Iterable<T?>.filterNotNull(): List<T>

            If yes then it’s not a defect.

            • Schyrsivochter says:

              I’m pretty sure that a type parameter declaration of <T> implicitly means <T : Any?>, which is exactly what we don’t want here. The resulting List should have a non-null element type, since it is the purpose of the function to filter out all nulls. The second signature you posted might even be invalid since T? is probably not a valid type if T is already nullable.

              • Tim van der Leeuw says:

                In that light it makes sense that the function signatures are defined as <T: Any> for filterNotNull().

                Wouldn’t that means that T? is by definition invalid in any context when the type definition is simply <T>, without any restriction?

  14. Cristan Meijer says:

    I’m very happy that Contracts has made it into the language 😀 I have myself not using extension methods like isNullOrEmpty() in quite a few cases because smart casts wouldn’t work.

  15. Pingback: Kotlin/Native v0.9 来啦! - kotlin Blog

Comments are closed.