Slide 1

Slide 1 text

Discovering Kotlin Contracts Nicola Corti @cortinico

Slide 2

Slide 2 text

1.3

Slide 3

Slide 3 text

The Problem

Slide 4

Slide 4 text

The Problem @Test fun testMyTokenLength() { }

Slide 5

Slide 5 text

The Problem @Test fun testMyTokenLength() { val token : String? = getMyToken(); }

Slide 6

Slide 6 text

The Problem @Test fun testMyTokenLength() { val token : String? = getMyToken(); assertNotNull(token) }

Slide 7

Slide 7 text

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?

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

Syntax

Slide 11

Slide 11 text

Syntax fun Int?.isValid(): Boolean { return this != null && this != 0 }

Slide 12

Slide 12 text

Syntax fun Int?.isValid(): Boolean { contract { } return this != null && this != 0 }

Slide 13

Slide 13 text

Syntax fun Int?.isValid(): Boolean { contract { // Place your effects here } return this != null && this != 0 }

Slide 14

Slide 14 text

Effects fun isValid(number: Int?) : Boolean Int? Boolean Effect1, Effect2, Effect3

Slide 15

Slide 15 text

Returns fun returns(): SimpleEffect fun returns(value: Any?): SimpleEffect fun returnsNotNull(): SimpleEffect interface SimpleEffect : Effect { infix fun implies(boolExpr: Boolean): ConditionalEffect } ConditionalEffect

Slide 16

Slide 16 text

Syntax fun Int?.isValid(): Boolean { contract { } return this != null && this != 0 }

Slide 17

Slide 17 text

Syntax fun Int?.isValid(): Boolean { contract { returns(true) } return this != null && this != 0 }

Slide 18

Slide 18 text

Syntax fun Int?.isValid(): Boolean { contract { returns(true) implies } return this != null && this != 0 }

Slide 19

Slide 19 text

Syntax fun Int?.isValid(): Boolean { contract { returns(true) implies (this@isValid != null) } return this != null && this != 0 }

Slide 20

Slide 20 text

Syntax val aInt : Int? = getAnInt() if (aInt.isValid()){ } else { }

Slide 21

Slide 21 text

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 { }

Slide 22

Slide 22 text

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 }

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

The Problem fun assertNotNull(actual: Any?) { }

Slide 26

Slide 26 text

The Problem fun assertNotNull(actual: Any?) { org.junit.Assert.assertNotNull(actual) }

Slide 27

Slide 27 text

The Problem fun assertNotNull(actual: Any?) { contract { returns() implies (actual != null) } org.junit.Assert.assertNotNull(actual) }

Slide 28

Slide 28 text

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 callsInPlace( lambda: Function, kind: InvocationKind = InvocationKind.UNKNOWN ): CallsInPlace

Slide 29

Slide 29 text

CallsInPlace InvocationKind.UNKNOWN

Slide 30

Slide 30 text

CallsInPlace InvocationKind.UNKNOWN InvocationKind.AT_MOST_ONCE

Slide 31

Slide 31 text

CallsInPlace InvocationKind.UNKNOWN InvocationKind.AT_MOST_ONCE InvocationKind.AT_LEAST_ONCE

Slide 32

Slide 32 text

CallsInPlace InvocationKind.UNKNOWN InvocationKind.AT_MOST_ONCE InvocationKind.AT_LEAST_ONCE InvocationKind.EXACTLY_ONCE

Slide 33

Slide 33 text

CallsInPlace fun main(){ val x : Int? run { x = 10 } println(x) }

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

CallsInPlace public inline fun T.run(block: T.() -> R): R { return block() }

Slide 36

Slide 36 text

CallsInPlace public inline fun T.run(block: T.() -> R): R { contract { } return block() }

Slide 37

Slide 37 text

CallsInPlace public inline fun T.run(block: T.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() }

Slide 38

Slide 38 text

CallsInPlace fun main(){ val x : Int? run { x = 10 } println(x) }

Slide 39

Slide 39 text

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.

Slide 40

Slide 40 text

Thanks! Nicola Corti @cortinico bit.ly/ktcontracts bit.ly/ktcontracts-slides