New Type Inference & Related Language Features Svetlana Isakova - - PowerPoint PPT Presentation

new type inference related language features
SMART_READER_LITE
LIVE PREVIEW

New Type Inference & Related Language Features Svetlana Isakova - - PowerPoint PPT Presentation

New Type Inference & Related Language Features Svetlana Isakova @sveta_isakova Agenda Experimental features Contracts New type inference Experimental features Experimental features Our goal: to let new features be tried by early


slide-1
SLIDE 1

Svetlana Isakova @sveta_isakova

New Type Inference & Related Language Features

slide-2
SLIDE 2

Agenda

Experimental features Contracts New type inference

slide-3
SLIDE 3

Experimental features

slide-4
SLIDE 4

Experimental features

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

slide-5
SLIDE 5

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

Automatic migration

slide-7
SLIDE 7

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

Experimental API for Libraries

  • can be publicly released as a part of the library
  • may break at any moment
slide-9
SLIDE 9

@ShinyNewAPI class Foo { ... }

Experimental API

@Experimental annotation class ShinyNewAPI

You can mark your shiny new class or function as experimental

slide-10
SLIDE 10

Using experimental API

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

slide-11
SLIDE 11
  • feedback loop for new features and API

Experimental: Summary

slide-12
SLIDE 12

Contracts

slide-13
SLIDE 13

Changes in standard library

slide-14
SLIDE 14

inline fun <R> run(block: () -> R): R = block()

Changes in standard library

slide-15
SLIDE 15

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

Changes in standard library

slide-16
SLIDE 16

Changes in standard library

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

slide-17
SLIDE 17

We know something about run, which the compiler doesn’t

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

slide-18
SLIDE 18

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

val s: String? = "" if (!s.isNullOrEmpty()) { s.first() }

We know something about isNullOrEmpty, which the compiler doesn’t

slide-20
SLIDE 20

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

Kotlin Contracts

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

slide-22
SLIDE 22

Variable initialization inside run

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

slide-23
SLIDE 23

Variable initialization inside run

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

slide-24
SLIDE 24

val s: String? = "" if (!s.isNullOrEmpty()) { s.first() }

Making smart casts even smarter

slide-25
SLIDE 25

val s: String? = "" if (!s.isNullOrEmpty()) { s.first() }

Making smart casts even smarter

slide-26
SLIDE 26

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

Contract: block lambda will be always called once

slide-27
SLIDE 27

Contract for calling inlined lambda in-place

run, let, with, apply, also takeIf, takeUnless, synchronized

slide-28
SLIDE 28

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

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

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

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

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

Kotlin Contract

extra information by developer & compiler uses this information for code analysis

experimental

slide-34
SLIDE 34

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

Why can’t compiler just implicitly infer such information?

slide-36
SLIDE 36

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

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

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

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

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

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

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
  • 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

New type inference

slide-45
SLIDE 45

New type inference

better and more powerful type inference new features are supported

slide-46
SLIDE 46

kotlin { experimental { newInference 'enable' } }

Might be turned on in Kotlin 1.3

slide-47
SLIDE 47

Kotlin libraries

  • Libraries should specify return types for public API
  • Overloaded functions must do the same thing
slide-48
SLIDE 48

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

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

SAM conversions for Kotlin functions

slide-51
SLIDE 51

fun handleInput(handler: Action<String>) { ... }

SAM conversions for Kotlin functions

public interface Action<T> { void execute(T target); }

slide-52
SLIDE 52

fun handleInput(handler: Action<String>) { ... }

SAM conversions for Kotlin functions

public interface Action<T> { 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

Support for several SAM arguments

Old inference:

  • bservable.zipWith(anotherObservable,

BiFunction { x, y -> x + y })

slide-54
SLIDE 54

Support for several SAM arguments

Old inference:

  • bservable.zipWith(anotherObservable,

BiFunction { x, y -> x + y }) class Observable { public final Observable zipWith( ObservableSource other, BiFunction zipper) {…} } SAM interfaces

slide-55
SLIDE 55

Support for several SAM arguments

Old inference:

  • bservable.zipWith(anotherObservable,

BiFunction { x, y -> x + y })

  • bservable.zipWith(anotherObservable) { x, y -> x + y }

New inference:

slide-56
SLIDE 56

Support for several SAM arguments

Old inference:

  • bservable.zipWith(anotherObservable,

BiFunction { x, y -> x + y })

  • bservable.zipWith(anotherObservable) { x, y -> x + y }

New inference:

slide-57
SLIDE 57

Builder inference

slide-58
SLIDE 58

Inference for sequence

val seq = sequence { yield(42) }

slide-59
SLIDE 59

Inference for sequence

val seq = sequence { yield(42) } : Sequence<Int>

slide-60
SLIDE 60

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

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

Inference for regular lambdas

people.map { person -> println("Processed: ${person.name}") person.age } List<Int> Int

Result type may depend on lambda return type

slide-63
SLIDE 63

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

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<Animal>

slide-65
SLIDE 65

Builder inference

automatically works for sequence in 1.3

  • pt in to use that for your functions
slide-66
SLIDE 66

Using @BuilderInference for your function

fun <T> buildList( @BuilderInference init: MutableList<T>.() -> Unit ): List<T> { return mutableListOf<T>().apply(init) } val list = buildList { add(cat) add(dog) } : List<Animal> Experimental

slide-67
SLIDE 67

Inference for call chains

slide-68
SLIDE 68

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

Inference for call chains

fun createMap() = MapBuilder() .put("answer", 42) .build() : Map<String, Int> Old inference:

One call is analyzed at a time

New inference:

A call chain is analyzed

slide-70
SLIDE 70

Intersection types

slide-71
SLIDE 71

Better inference for intersection types

interface Drownable interface Throwable fun <T> throwIntoRiver(thing: T) where T : Drownable, T : Throwable { println("Bye, $thing") }

slide-72
SLIDE 72

Better inference for intersection types

interface Drownable interface Throwable fun <T> throwIntoRiver(thing: T) where T : Drownable, T : Throwable { println("Bye, $thing") } if (something is Drownable && something is Throwable) { throwIntoRiver(something) }

slide-73
SLIDE 73

Drownable & Throwable

Better inference for intersection types

interface Drownable interface Throwable fun <T> throwIntoRiver(thing: T) where T : Drownable, T : Throwable { println("Bye, $thing") } if (something is Drownable && something is Throwable) { throwIntoRiver(something) }

slide-74
SLIDE 74

Intersection type to denote not-nullable generic type

T!! = T & Any

slide-75
SLIDE 75

Proper type for T!!

fun <T> describe(x: T) { println(x!!::class.simpleName) }

slide-76
SLIDE 76

Proper type for T!!

fun <T> describe(x: T) { println(x!!::class.simpleName) } T!!

slide-77
SLIDE 77

Proper type for T!!

fun <T> describe(x: T) { println(x!!::class.simpleName) } T!! T!! fun <T> describe(x: T) { if (x != null) { println(x::class.simpleName) } }

slide-78
SLIDE 78

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

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

Improving assertNotNull: return asserted value

slide-80
SLIDE 80

fun <T : Any> 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

Improving assertNotNull: return asserted value

fun <S: CharSequence> testInput(input: S?) { assertTrue(assertNotNull(input).all { it.isDigit() }) }

slide-82
SLIDE 82

Improving assertNotNull: return asserted value

fun <S: CharSequence> testInput(input: S?) { assertTrue(assertNotNull(input).all { it.isDigit() }) } S!!

S!! = S & Any

slide-83
SLIDE 83

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

Have a nice Kotlin!

#kotlinconf18