Slide 1

Slide 1 text

Svetlana Isakova @sveta_isakova New Type Inference & Related Language Features

Slide 2

Slide 2 text

Agenda Experimental features Contracts New type inference

Slide 3

Slide 3 text

Experimental features

Slide 4

Slide 4 text

Experimental features Our goal: to let new features be tried by early adopters as soon as possible

Slide 5

Slide 5 text

import kotlinx.coroutines.experimental.* import kotlinx.coroutines.* Example: Coroutines Kotlin 1.3 Kotlin 1.2 might be stable enough, but no backward compatibility guarantees backward compatibility guarantees

Slide 6

Slide 6 text

Automatic migration

Slide 7

Slide 7 text

Experimental Language Features • you need to explicitly opt in at the call site to use experimental features kotlin { experimental { coroutines 'enable' } }

Slide 8

Slide 8 text

Experimental API for Libraries • can be publicly released as a part of the library • may break at any moment

Slide 9

Slide 9 text

@ShinyNewAPI class Foo { ... } Experimental API @Experimental annotation class ShinyNewAPI You can mark your shiny new class or function as experimental

Slide 10

Slide 10 text

Using experimental API @UseExperimental(ShinyNewAPI::class) fun doSomethingImportant() { val foo = Foo() ... }

Slide 11

Slide 11 text

• feedback loop for new features and API Experimental: Summary

Slide 12

Slide 12 text

Contracts

Slide 13

Slide 13 text

Changes in standard library

Slide 14

Slide 14 text

inline fun run(block: () -> R): R = block() Changes in standard library

Slide 15

Slide 15 text

inline fun run(block: () -> R): R = block() inline fun run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } Changes in standard library

Slide 16

Slide 16 text

Changes in standard library inline fun run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } inline fun run(block: () -> R): R = block()

Slide 17

Slide 17 text

We know something about run, which the compiler doesn’t val answer: Int run { answer = 42 } println(answer)

Slide 18

Slide 18 text

We know something about run, which the compiler doesn’t val answer: Int run { answer = 42 } println(answer) Compiler error: Captured values initialization is forbidden due to possible reassignment

Slide 19

Slide 19 text

val s: String? = "" if (!s.isNullOrEmpty()) { s.first() } We know something about isNullOrEmpty, which the compiler doesn’t

Slide 20

Slide 20 text

val s: String? = "" if (!s.isNullOrEmpty()) { s.first() } Compiler error: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String? We know something about isNullOrEmpty, which the compiler doesn’t

Slide 21

Slide 21 text

Kotlin Contracts …allow to share extra information about code semantics with the compiler

Slide 22

Slide 22 text

Variable initialization inside run val answer: Int run { answer = 42 } println(answer)

Slide 23

Slide 23 text

Variable initialization inside run val answer: Int run { answer = 42 } println(answer) ✓

Slide 24

Slide 24 text

val s: String? = "" if (!s.isNullOrEmpty()) { s.first() } Making smart casts even smarter

Slide 25

Slide 25 text

val s: String? = "" if (!s.isNullOrEmpty()) { s.first() } Making smart casts even smarter ✓

Slide 26

Slide 26 text

inline fun run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } Contract: block lambda will be always called once

Slide 27

Slide 27 text

Contract for calling inlined lambda in-place run, let, with, apply, also takeIf, takeUnless, synchronized

Slide 28

Slide 28 text

fun String?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) } return this == null || this.length == 0 } Contract: if the function returns false, the receiver is not-null

Slide 29

Slide 29 text

fun String?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) } return this == null || this.length == 0 } Contract: if the function returns false, the receiver is not-null val s: String? = "" if (!s.isNullOrEmpty()) { s.first() } ✓

Slide 30

Slide 30 text

fun String?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) } return this == null || this.length == 0 } Contract: if the function returns false, the receiver is not-null

Slide 31

Slide 31 text

this: ContractBuilder fun String?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) } return this == null || this.length == 0 } Contract: if the function returns false, the receiver is not-null

Slide 32

Slide 32 text

Contract: if the function returns a given value, a condition is satisfied isNullOrEmpty, isNullOrBlank kotlin.test: assertTrue, assertFalse, assertNotNull check, checkNotNull, require, requireNotNull

Slide 33

Slide 33 text

Kotlin Contract extra information by developer & compiler uses this information for code analysis experimental

Slide 34

Slide 34 text

Kotlin Contract extra information by developer & compiler uses this information for code analysis & checking that the information is correct at compile time or runtime to be supported

Slide 35

Slide 35 text

Why can’t compiler just implicitly infer such information?

Slide 36

Slide 36 text

Why can’t compiler just implicitly infer such information? Because then such implicitly inferred information: - can be implicitly changed - can implicitly break code depending on it

Slide 37

Slide 37 text

Why can’t compiler just implicitly infer such information? Because then such implicitly inferred information: - can be implicitly changed - can implicitly break code depending on it Contract = explicit statement about function behaviour

Slide 38

Slide 38 text

Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null) { if (actual == null) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull(input) assertTrue(input!!.any { it.isDigit() }) }

Slide 39

Slide 39 text

Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null) { if (actual == null) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull(input) assertTrue(input!!.any { it.isDigit() }) }

Slide 40

Slide 40 text

Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null) { contract { returns() implies (actual != null) } if (actual == null) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull(input) assertTrue(input.all { it.isDigit() }) }

Slide 41

Slide 41 text

Experimental Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null) { contract { returns() implies (actual != null) } if (actual == null) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull(input) assertTrue(input.all { it.isDigit() }) }

Slide 42

Slide 42 text

Experimental Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null) { contract { returns() implies (actual != null) } if (actual == null) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull(input) assertTrue(input.all { it.isDigit() }) }

Slide 43

Slide 43 text

• handy functions (run, isEmptyOrNull) are even more useful • contract DSL will change • you can go and try it out Contracts: Summary

Slide 44

Slide 44 text

New type inference

Slide 45

Slide 45 text

New type inference better and more powerful type inference new features are supported

Slide 46

Slide 46 text

kotlin { experimental { newInference 'enable' } } Might be turned on in Kotlin 1.3

Slide 47

Slide 47 text

Kotlin libraries • Libraries should specify return types for public API • Overloaded functions must do the same thing

Slide 48

Slide 48 text

Kotlin libraries • Libraries should specify return types for public API • Overloaded functions must do the same thing turn on an IDE inspection “Public API declaration has implicit return type”

Slide 49

Slide 49 text

New type inference • SAM conversions for Kotlin functions • better inference for builders • better inference for call chains • better inference for intersection types

Slide 50

Slide 50 text

SAM conversions for Kotlin functions

Slide 51

Slide 51 text

fun handleInput(handler: Action) { ... } SAM conversions for Kotlin functions public interface Action { void execute(T target); }

Slide 52

Slide 52 text

fun handleInput(handler: Action) { ... } SAM conversions for Kotlin functions public interface Action { void execute(T target); } You can pass a lambda as an argument when a Java SAM-interface is expected: handleInput { println(it) }

Slide 53

Slide 53 text

Support for several SAM arguments Old inference: observable.zipWith(anotherObservable, BiFunction { x, y -> x + y })

Slide 54

Slide 54 text

Support for several SAM arguments Old inference: observable.zipWith(anotherObservable, BiFunction { x, y -> x + y }) class Observable { public final Observable zipWith( ObservableSource other, BiFunction zipper) {…} } SAM interfaces

Slide 55

Slide 55 text

Support for several SAM arguments Old inference: observable.zipWith(anotherObservable, BiFunction { x, y -> x + y }) observable.zipWith(anotherObservable) { x, y -> x + y } New inference:

Slide 56

Slide 56 text

Support for several SAM arguments Old inference: observable.zipWith(anotherObservable, BiFunction { x, y -> x + y }) observable.zipWith(anotherObservable) { x, y -> x + y } New inference: ✓

Slide 57

Slide 57 text

Builder inference

Slide 58

Slide 58 text

Inference for sequence val seq = sequence { yield(42) }

Slide 59

Slide 59 text

Inference for sequence val seq = sequence { yield(42) } : Sequence

Slide 60

Slide 60 text

Inference for regular lambdas people.map { person -> println("Processed: ${person.name}") person.age } Result type may depend on lambda return type

Slide 61

Slide 61 text

Inference for regular lambdas people.map { person -> println("Processed: ${person.name}") person.age } Int Result type may depend on lambda return type

Slide 62

Slide 62 text

Inference for regular lambdas people.map { person -> println("Processed: ${person.name}") person.age } List Int Result type may depend on lambda return type

Slide 63

Slide 63 text

Inference for builder lambdas val seq = sequence { yield(cat) println("adding more elements") yield(dog) } Result type may depend only on lambda return type Result type may depend on calls inside lambda

Slide 64

Slide 64 text

Inference for builder lambdas val seq = sequence { yield(cat) println("adding more elements") yield(dog) } Result type may depend only on lambda return type Result type may depend on calls inside lambda : Sequence

Slide 65

Slide 65 text

Builder inference automatically works for sequence in 1.3 opt in to use that for your functions

Slide 66

Slide 66 text

Using @BuilderInference for your function fun buildList( @BuilderInference init: MutableList.() -> Unit ): List { return mutableListOf().apply(init) } val list = buildList { add(cat) add(dog) } : List Experimental

Slide 67

Slide 67 text

Inference for call chains

Slide 68

Slide 68 text

Inference for call chains fun createMap() = MapBuilder() .put("answer", 42) .build() Old inference: One call is analyzed at a time New inference: A call chain is analyzed

Slide 69

Slide 69 text

Inference for call chains fun createMap() = MapBuilder() .put("answer", 42) .build() : Map Old inference: One call is analyzed at a time New inference: A call chain is analyzed

Slide 70

Slide 70 text

Intersection types

Slide 71

Slide 71 text

Better inference for intersection types interface Drownable interface Throwable fun throwIntoRiver(thing: T) where T : Drownable, T : Throwable { println("Bye, $thing") }

Slide 72

Slide 72 text

Better inference for intersection types interface Drownable interface Throwable fun throwIntoRiver(thing: T) where T : Drownable, T : Throwable { println("Bye, $thing") } if (something is Drownable && something is Throwable) { throwIntoRiver(something) }

Slide 73

Slide 73 text

Drownable & Throwable Better inference for intersection types interface Drownable interface Throwable fun throwIntoRiver(thing: T) where T : Drownable, T : Throwable { println("Bye, $thing") } if (something is Drownable && something is Throwable) { throwIntoRiver(something) }

Slide 74

Slide 74 text

Intersection type to denote not-nullable generic type T!! = T & Any

Slide 75

Slide 75 text

Proper type for T!! fun describe(x: T) { println(x!!::class.simpleName) }

Slide 76

Slide 76 text

Proper type for T!! fun describe(x: T) { println(x!!::class.simpleName) } T!!

Slide 77

Slide 77 text

Proper type for T!! fun describe(x: T) { println(x!!::class.simpleName) } T!! T!! fun describe(x: T) { if (x != null) { println(x::class.simpleName) } }

Slide 78

Slide 78 text

Improving assertNotNull fun assertNotNull(actual: Any?, message: String? = null) { if (actual == null) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull(input) assertTrue(input!!.all { it.isDigit() }) }

Slide 79

Slide 79 text

fun testInput(input: String?) { assertTrue(assertNotNull(input).all { it.isDigit() }) } Improving assertNotNull: return asserted value

Slide 80

Slide 80 text

fun assertNotNull( actual: T?, message: String? = null ): T { if (actual == null) { throw AssertionError( message ?: "Value must not be null" ) } return actual } fun testInput(input: String?) { assertTrue(assertNotNull(input).all { it.isDigit() }) } Improving assertNotNull: return asserted value

Slide 81

Slide 81 text

Improving assertNotNull: return asserted value fun testInput(input: S?) { assertTrue(assertNotNull(input).all { it.isDigit() }) }

Slide 82

Slide 82 text

Improving assertNotNull: return asserted value fun testInput(input: S?) { assertTrue(assertNotNull(input).all { it.isDigit() }) } S!! S!! = S & Any

Slide 83

Slide 83 text

New type inference: summary • new features are supported • will be used by default in the future • you can go and try it out

Slide 84

Slide 84 text

Have a nice Kotlin! #kotlinconf18