Kotlin Contracts – cooperation with the compiler

by coding flower

Sometimes compiler needs help and more information. Kotlin contracts are a way to transmit more information to the compiler.

Kotlin contracts with the custom contract are experimental, but the stdlib already uses it.

This is a new language mechanism, it allows developers to pass more detailed information to the compiler and let the compiler utilize and analyze more data.

The new mechanism gives new possibilities:

  • improving smartcasts analysis
  • improve variable initialization analysis

Improve smartcasts analysis

Smartcast dissappears whenever we extract any checks to a new function, to keep the same behavior we can apply kotlin contracts.

Let's imagine the situation when we want to make object validation before processing. Validation is not so simple so we decide to extract it to the new function.

private fun validateUserWithoutContract(user: User?) {
    if (user == null) {
        throw Exception("We have big problem!")
    }
        ...
}

When we try to reach properties of a given user object without a safe call, we will meet a compilation error.

fun processWithoutContract(user: User?) {
    validateUserWithoutContract(user)
    println(user.name)        // Compilation error
}

We know that this object cannot be null because we already make a null-check, but the compiler doesn't know that. Kotlin contracts allow us to pass this information to the compiler.

@ExperimentalContracts
private fun validateUserWithContract(user: User?) {
    contract {
        returns() implies (user != null)
    }
    if (user == null) {
        error("We have big problem!")
    }
}

With this contract assumption, we can use user.name to reach name property, without a safe-call outside of the function, so processWithoutContract method will compile without any errors. The example above is the good practice of using this feature, another good way of utilization is casting.

Improve variable initialization analysis for higher-order functions

Let's check the second part of the contracts, we can tell the compiler how many times a function will be called by callsInPlace and InvocationKind .

Kotlin contracts have four invocation kinds:

  • UNKNOWN - can't initialize var or val, doesn't have to return.
  • AT_MOST_ONCE - same as UNKNOWN.
  • AT_LEAST_ONCE - can initialize var, but can't be used to initialize val, has to return after returned in the block.
  • EXACTLY_ONCE- can initialize val and var, but also has to return after returned in the block.

Let's have a look at run function:

@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

This function uses EXACTLY_ONCE so it means that you can use it to initialization of var and val variables. That's why the below code will compile, without this contract statement compiler will complain about Captured values initialization is forbidden due to possible reassignment and suggest changing to var instead of val.

This example illustrates one of the most common ways to use kotlin contracts, the initialization of objects in lambdas.

fun main() {
    val website: Website
    run {
        website = Website("www.codingflower.com")
    }
    print(website)
}

Limitations

  • Contract has to be on the very first lines of the code.
  • It can be used only on top functions or member functions.
  • There is no verification of the contracts.

Summary

This mechanism works in compile-time, the developer may provide additional guarantees or restrictions, which could be utilized by the compiler to perform analysis. This is the solution for the problem when we know that the object is not null but we had to use a safe call because of removed smartcasting. Some of the functions in stdlib are annotated with contracts. This contract concept is nothing new because this mechanism is already implemented in C++.

Related Posts

Leave a Comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More