Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Discovering Kotlin Contracts

Discovering Kotlin Contracts

Slides for me lightning talk on Kotlin Contracts @ GDG Hamburg Android

Nicola Corti

October 02, 2018
Tweet

More Decks by Nicola Corti

Other Decks in Technology

Transcript

  1. 1.3

  2. The Problem @Test fun testMyTokenLength() { val token : String?

    = getMyToken(); assertNotNull(token) assertEquals(42, token.length) } Error(5, 22): Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
  3. The Problem @Test fun testMyTokenLength() { val token : String?

    = getMyToken(); assertNotNull(token) assertEquals(42, token?.length) }
  4. Kotlin Contracts • The Compiler is not smart enough to


    understand the context • You know something that the compiler doesn’t know • We can provide this extra knowledge with a Kotlin Contract • The goal is to write better/cleaner code Experimental features
  5. Syntax fun Int?.isValid(): Boolean { contract { // Place your

    effects here } return this != null && this != 0 }
  6. Returns fun returns(): SimpleEffect fun returns(value: Any?): SimpleEffect fun returnsNotNull():

    SimpleEffect interface SimpleEffect : Effect { infix fun implies(boolExpr: Boolean): ConditionalEffect } ConditionalEffect
  7. Syntax val aInt : Int? = getAnInt() if (aInt.isValid()){ //

    Here the compiler knows that `aInt` is not nullable // thanks to the contract on `isValid` aInt.absoluteValue } else { }
  8. Syntax val aInt : Int? = getAnInt() if (aInt.isValid()){ //

    Here the compiler knows that `aInt` is not nullable // thanks to the contract on `isValid` aInt.absoluteValue } else { // Here the compiler has no extra information since // the `isValid` has only a `returns(true)` effect aInt?.absoluteValue }
  9. The Problem @Test fun testMyTokenLenght() { val token : String?

    = getMyToken(); assertNotNull(token) assertEquals(42, token?.length) }
  10. The Problem @Test fun testMyTokenLenght() { val token : String?

    = getMyToken(); assertNotNull(token) assertEquals(42, token.length) }
  11. The Problem fun assertNotNull(actual: Any?) { contract { returns() implies

    (actual != null) } org.junit.Assert.assertNotNull(actual) }
  12. CallsInPlace • lambda will not be called after the call

    to owner-function is finished. • lambda will not be passed to another function that 
 doesn’t have a similar contract. • lambda will be called the specified amount of times (kind) fun <R> callsInPlace( lambda: Function<R>, kind: InvocationKind = InvocationKind.UNKNOWN ): CallsInPlace
  13. CallsInPlace fun main(){ val x : Int? run { x

    = 10 } println(x) } Error:(6, 18) Variable ‘x’ must be initialized Error:(4, 8) Captured values initialization is forbidden due to possible reassignment
  14. CallsInPlace public inline fun <T, R> T.run(block: T.() -> R):

    R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() }
  15. Limitations • They are not verified. 
 You’re responsible for

    writing correct contracts. • returns(value) and implies allowed values are limited • Only to top-level and block functions.