Slide 1

Slide 1 text

Kotlin 2.0 and beyond @antonarhipov

Slide 2

Slide 2 text

July 20, 2011 - Kotlin announced at JVMLS February 15, 2016 - Kotlin 1.0 May 21, 2024 - Kotlin 2.0

Slide 3

Slide 3 text

July 20, 2011 - Kotlin announced at JVMLS February 15, 2016 - Kotlin 1.0 May 21, 2024 - Kotlin 2.0

Slide 4

Slide 4 text

July 20, 2011 - Kotlin announced at JVMLS February 15, 2016 - Kotlin 1.0 2020 - Anton joined Kotlin team

Slide 5

Slide 5 text

July 20, 2011 - Kotlin announced at JVMLS February 15, 2016 - Kotlin 1.0 2020 ANTON KOTLIN FANBOYS Reality ANTON JAVA DEVELOPERS Expectation Me becoming Kotlin advocate:

Slide 6

Slide 6 text

July 20, 2011 - Kotlin announced at JVMLS February 15, 2016 - Kotlin 1.0 May 21, 2024 - Kotlin 2.0

Slide 7

Slide 7 text

Features added after Kotlin 1.0: - Multiplatform projects - Coroutines - Inline / Value classes - Trailing comma - fun interfaces - ...

Slide 8

Slide 8 text

Features added after Kotlin 1.0: - Multiplatform projects - Coroutines - Inline / Value classes - Trailing comma - fun interfaces - Type aliases - Sealed classes & interfaces - Contracts - break/continue inside when - Exhaustive when statements - Builder inference - . . < operator - Data objects

Slide 9

Slide 9 text

Features added after Kotlin 1.0: - Multiplatform projects - Coroutines - Inline / Value classes - Trailing comma - fun interfaces - Type aliases - Sealed classes & interfaces - Contracts - break/continue inside when - Exhaustive when statements - Builder inference - ..< operator - Data objects - provideDelegate - Bound callable references - Destructuring in lambdas - Array literals in annotations - Local lateinit variables - Opt-in annotations - De fi nitely non-nullable types - Instantiation of annotation classes - Support for JSpecify - suspend functions as supertypes - Secondary constructors for inline value classes

Slide 10

Slide 10 text

class MyFun(var param: T): () - > Result { override fun invoke(): Result { / /... } } fun handle(handler: () -> Result) { //. .. } ‘C’ for consistency!

Slide 11

Slide 11 text

class MyFun(var param: T): () - > Result { override fun invoke(): Result { / /... } } fun handle(handler: () -> Result) { //. .. } ‘C’ for consistency! Functional type

Slide 12

Slide 12 text

class MyFun(var param: T): suspend () - > Result { override suspend fun invoke(): Result { / /... } } fun handle(handler: suspend () -> Result) { //. .. } ‘C’ for consistency! Forbidden before 1.6

Slide 13

Slide 13 text

class MyFun(var param: T): suspend () - > Result { override suspend fun invoke(): Result { / /... } } fun handle(handler: suspend () -> Result) { //. .. } ‘C’ for consistency! Fixed in 1.6

Slide 14

Slide 14 text

fun process(supplier: Supplier) = supplier.get() fun main() { process { "hello!" } } ‘C’ for consistency!

Slide 15

Slide 15 text

fun process(supplier: Supplier) = supplier.get() fun main() { process { "hello!" } } SAM type SAM conversion ‘C’ for consistency!

Slide 16

Slide 16 text

fun process(supplier: Supplier = Supplier { "hello!" }) = supplier.get() fun main() { val supplier: Supplier = Supplier { "hello!" } process(supplier) } ‘C’ for consistency!

Slide 17

Slide 17 text

fun process(supplier: Supplier = Supplier { "hello!" }) = supplier.get() fun main() { val supplier: Supplier = Supplier { "hello!" } process(supplier) } Type name required ‘C’ for consistency!

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

What’s FE?

Slide 22

Slide 22 text

FE stands for compiler’s Frontend

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

K2: The new Kotlin compiler - why?

Slide 25

Slide 25 text

K2: The new Kotlin compiler - why? 1. A few language features have appeared unexpectedly in Kotlin Hard to maintain and evolve the compiler 2. Interaction with compiler and IDEs Many ad-hoc solutions, no strict contracts, and no stable API 3. Compilation time performance

Slide 26

Slide 26 text

K2: The new Kotlin compiler - why? 1. A few language features have appeared unexpectedly in Kotlin Hard to maintain and evolve the compiler 2. Interaction with compiler and IDEs Many ad-hoc solutions, no strict contracts, and no stable API 3. Compilation time performance

Slide 27

Slide 27 text

K2: The new Kotlin compiler - why? 1. A few language features have appeared unexpectedly in Kotlin Hard to maintain and evolve the compiler 2. Interaction with compiler and IDEs Many ad-hoc solutions, no strict contracts, and no stable API 3. Compilation time performance

Slide 28

Slide 28 text

https: // youtu.be/db19VFLZqJM https: // youtu.be/iTdJJq_LyoY

Slide 29

Slide 29 text

Kotlin 2.0 More than 80 features in the di ff erent subsystems Around 25 and small improvements within the language Main focus is on correctness and performance

Slide 30

Slide 30 text

Frontend Intermediate Representation (FIR)

Slide 31

Slide 31 text

if (condition) { println("Hello") } for (n in list) { println(n) } val (a, b) = "a" to "b" Frontend Intermediate Representation (FIR)

Slide 32

Slide 32 text

if (condition) { println("Hello") } when { condition -> println("Hello") } for (n in list) { println(n) } val = list.interator() while(.hasNext()){ val s = .next() println(s) } val (a, b) = "a" to "b" val = "a" to "b" val a = pair.component1() val b = pair.component2() Frontend Intermediate Representation (FIR)

Slide 33

Slide 33 text

fun mutate(ml: MutableList) { ml[0] = ml[0] + 1 } Combination of Long and Integer Literal Types Frontend Intermediate Representation (FIR)

Slide 34

Slide 34 text

fun mutate(ml: MutableList) { ml[0] = ml[0] + 1 } Combination of Long and Integer Literal Types Long Integer Literal Type Frontend Intermediate Representation (FIR)

Slide 35

Slide 35 text

fun mutate(ml: MutableList) { ml[0] += 1 } Combination of Long and Integer Literal Types Error: 1L is required // Error in Kotlin 1.x Frontend Intermediate Representation (FIR)

Slide 36

Slide 36 text

fun mutate(ml: MutableList) { ml[0] += 1 } Combination of Long and Integer Literal Types // OK in 2.0 Desugared into: ml.set(0, ml.get(0).plus(1)) Frontend Intermediate Representation (FIR)

Slide 37

Slide 37 text

Combination of nullable operator-calls class Box(val ml: MutableList) fun mutate(box: Box?) { box ?. ml[0] += 1 // Error in 1.x box ?. ml[0] += 1L // Error in 1.x } Frontend Intermediate Representation (FIR)

Slide 38

Slide 38 text

Combination of nullable operator-calls class Box(val ml: MutableList) fun mutate(box: Box?) { box ?. ml[0] += 1 // OK in 2.0 } box ?. run { ml.set(0, ml.get(0).plus(1))} Desugared into: Frontend Intermediate Representation (FIR)

Slide 39

Slide 39 text

New control fl ow engine read: more smart-casts! - KT-7186 Smart cast for captured variables inside changing closures of inline functions - KT-4113 Smart casts for properties to not-null functional types at invoke calls - KT-25747 DFA variables: propagate smart cast results from local variables - KT-1982 Smart cast to a common supertype of subject types after || (OR operator) - ...

Slide 40

Slide 40 text

Smart-casts

Slide 41

Slide 41 text

class Cat { fun purr() { println("Purr purr") } } fun petAnimal(animal: Any) { if (animal is Cat) { animal.purr() } } Smart-casts

Slide 42

Slide 42 text

class Cat { fun purr() { println("Purr purr") } } fun petAnimal(animal: Any) { if (animal is Cat) { animal.purr() } } Smart-casts

Slide 43

Slide 43 text

class Cat { fun purr() { println("Purr purr") } } fun petAnimal(animal: Any) { val isCat = animal is Cat if (isCat) { animal.purr() // Error in Kotlin 1.x } } Smart-casts from variables

Slide 44

Slide 44 text

class Cat { fun purr() { println("Purr purr") } } fun petAnimal(animal: Any) { val isCat = animal is Cat if (isCat) { animal.purr() // Error in Kotlin 1.x } } Smart-casts from variables Kotlin 1.x: variables don't carry any data fl ow information

Slide 45

Slide 45 text

class Cat { fun purr() { println("Purr purr") } } fun petAnimal(animal: Any) { val isCat = animal is Cat if (isCat) { animal.purr() // OK in Kotlin 2.0 } } Smart-casts from variables Kotlin 2.0: synthetic data fl ow variables propagate information about smart-casts

Slide 46

Slide 46 text

Smart-casts from variables class Card(val holder: String?) fun findHolder(card: Any): String { val cardWithHolder = card is Card && !card.holder.isNullOrEmpty() return when { cardWithHolder -> { card.holder } else -> "none" } }

Slide 47

Slide 47 text

Smart-casts from variables class Card(val holder: String?) fun findHolder(card: Any): String { val cardWithHolder = card is Card && !card.holder.isNullOrEmpty() return when { cardWithHolder -> { card.holder } else -> "none" } } Any -> Card String? -> String Smart-cast to String

Slide 48

Slide 48 text

Smart-casts inside closures of inline lambdas

Slide 49

Slide 49 text

fun indexOfMax(a: IntArray): Int? { var maxI: Int? = null a.forEachIndexed { i, value -> if (maxI == null || a[maxI !! ] <= value) { maxI = i } } return maxI } Smart-casts inside closures of inline lambdas

Slide 50

Slide 50 text

fun indexOfMax(a: IntArray): Int? { var maxI: Int? = null a.forEachIndexed { i, value -> if (maxI == null || a[maxI !! ] <= value) { maxI = i } } return maxI } Smart-casts inside closures of inline lambdas Kotlin 1.x: Smart cast to 'Int' is impossible. 'maxI' is a local variable that is captured by a changing closure

Slide 51

Slide 51 text

fun indexOfMax(a: IntArray): Int? { var maxI: Int? = null a.forEachIndexed { i, value -> if (maxI == null || a[maxI] <= value) { maxI = i } } return maxI } Smart-casts inside closures of inline lambdas Kotlin 2.0: treats inline functions as having an implicit callInPlace contract

Slide 52

Slide 52 text

Smart-casts after || interface Status { fun signal() } interface Ok : Status interface Postponed : Status interface Declined : Status

Slide 53

Slide 53 text

interface Status { fun signal() } interface Ok : Status interface Postponed : Status interface Declined : Status Smart-casts after ||

Slide 54

Slide 54 text

Smart-casts after || fun alert(signalStatus: Any) { if (signalStatus is Postponed || signalStatus is Declined) { signalStatus.signal() // Error 1.x: the inferred type is Any } }

Slide 55

Slide 55 text

Smart-casts after || fun alert(signalStatus: Any) { if (signalStatus is Postponed || signalStatus is Declined) { signalStatus.signal() // OK in 2.0 } }

Slide 56

Slide 56 text

Smart-casts after || fun alert(signalStatus: Any) { if (signalStatus is Postponed || signalStatus is Declined) { signalStatus.signal() // OK in 2.0 } } Kotlin 2.0: merge to a common supertype (e.g. Status)

Slide 57

Slide 57 text

This slide is intentionally left blank

Slide 58

Slide 58 text

What's next for Kotlin?

Slide 59

Slide 59 text

What's next for Kotlin? Guards: pattern matching without binding Context-sensitive resolution Data classes improvements E ff ect system capabilities Data recognition and decunstruction

Slide 60

Slide 60 text

What's next for Kotlin? Guards: pattern matching without binding Context-sensitive resolution Data classes improvements E ff ect system capabilities Data recognition and decunstruction Language features for libraries Union types for errors "dataarg" classes Context parameters

Slide 61

Slide 61 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } val searchPanel = selectedSearchPanel() when { searchPanel is SearchPanel.NewsPanel && !searchPanel.isBlocked -> item { NewResult(lastData) } searchPanel == SearchPanel.SpeakersPanel - > item { } searchPanel == SearchPanel.TalksPanel -> item { } } } }

Slide 62

Slide 62 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } val searchPanel = selectedSearchPanel() when { searchPanel is SearchPanel.NewsPanel && !searchPanel.isBlocked -> item { NewResult(lastData) } searchPanel == SearchPanel.SpeakersPanel - > item { } searchPanel == SearchPanel.TalksPanel -> item { } } } }

Slide 63

Slide 63 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } val searchPanel = selectedSearchPanel() when { searchPanel is SearchPanel.NewsPanel && !searchPanel.isBlocked -> item { NewResult(lastData) } searchPanel == SearchPanel.SpeakersPanel - > item { } searchPanel == SearchPanel.TalksPanel -> item { } } } }

Slide 64

Slide 64 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } when(val searchPanel = selectedSearchPanel()) { is SearchPanel.NewsPanel & & !searchPanel.isBlocked - > item { NewResult(lastData) } is SearchPanel.SpeakersPanel -> item { } is SearchPanel.TalksPanel -> item { } } } }

Slide 65

Slide 65 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } when(val searchPanel = selectedSearchPanel()) { is SearchPanel.NewsPanel & & !searchPanel.isBlocked - > item { NewResult(lastData) } is SearchPanel.SpeakersPanel -> item { } is SearchPanel.TalksPanel -> item { } } } } Error: expecting ' -> ' &&

Slide 66

Slide 66 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } when(val searchPanel = selectedSearchPanel()) { is SearchPanel.NewsPanel if !searchPanel.isBlocked - > item { NewResult(lastData) } is SearchPanel.SpeakersPanel -> item { } is SearchPanel.TalksPanel -> item { } } } } Guarded conditions: KEEP-371 Coming as Beta in 2.1 if

Slide 67

Slide 67 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } when(val searchPanel = selectedSearchPanel()) { is SearchPanel.NewsPanel if !searchPanel.isBlocked - > item { NewResult(lastData) } is SearchPanel.SpeakersPanel -> item { } is SearchPanel.TalksPanel -> item { } } } } Can we do better?

Slide 68

Slide 68 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } when(val searchPanel = selectedSearchPanel()) { is SearchPanel.NewsPanel if !searchPanel.isBlocked - > item { NewResult(lastData) } is SearchPanel.SpeakersPanel -> item { } is SearchPanel.TalksPanel -> item { } } } } Can we do better?

Slide 69

Slide 69 text

@Composable fun DisplayLastSearchResultByPanelType( searchData: Sequence, id: String ) { LazyColumn { val lastData = searchData.last { it.id == id } when(val searchPanel = selectedSearchPanel()) { is NewsPanel if !searchPanel.isBlocked - > item { NewResult(lastData) } is SpeakersPanel -> item { } is TalksPanel -> item { } } } } Coming as experimental in 2.2 Context-sensitive resolution KEEP-379 No extra quali fi er

Slide 70

Slide 70 text

Context-sensitive resolution enum class Status { Ok, Fail } fun process(status: Status = Status.Ok) { ... }

Slide 71

Slide 71 text

Context-sensitive resolution enum class Status { Ok, Fail } fun process(status: Status = Status.Ok) { ... }

Slide 72

Slide 72 text

Context-sensitive resolution enum class Status { Ok, Fail } fun process(status: Status = Ok) { ... }

Slide 73

Slide 73 text

Descructuring

Slide 74

Slide 74 text

val user = User("Anton", "Arhipov") val (firstName, surname) = user println(surname) // Arhipov println(firstName) // Anton Descructuring data class User(val name: String, val lastName: String)

Slide 75

Slide 75 text

val user = User("Anton", "Arhipov") val (surname, firstName) = user println(surname) // Anton println(firstName) // Arhipov Descructuring data class User(val name: String, val lastName: String)

Slide 76

Slide 76 text

val user = User("Anton", "Arhipov") val (surname, firstName) = user Name-based descructuring Error in 2.x: -“surname” doesn’t match the property “name” -“ fi rstName” doesn’t match the property “lastName” data class User(val name: String, val lastName: String)

Slide 77

Slide 77 text

How do we evolve libraries?

Slide 78

Slide 78 text

LazyColumn { val lastData = searchData.last { it.id == id } val searchPanel = selectedSearchPanel() when { searchPanel is SearchPanel.NewsPanel && !searchPanel.isBlocked -> item { NewResult(lastData) } searchPanel == SearchPanel.SpeakersPanel - > item { } searchPanel == SearchPanel.TalksPanel -> item { } } }

Slide 79

Slide 79 text

@Composable fun LazyColumn( modifier: Modifier = Modifier, state: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit ) { /* ... */ }

Slide 80

Slide 80 text

API evolution of optional parameters Adding new parameter with a default value can be backwards incompatible, leading to new overlads of the function

Slide 81

Slide 81 text

@Composable fun LazyColumn( modifier: Modifier = Modifier, state: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(0.dp), reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit ) { /* ... */ }

Slide 82

Slide 82 text

dataarg class ColumnSettings( val contentPadding: PaddingValues = PaddingValues(0.dp), val reverseLayout: Boolean = false, val verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, val horizontalAlignment: Alignment.Horizontal = Alignment.Start, val userScrollEnabled: Boolean = true ) Extensible data arguments

Slide 83

Slide 83 text

dataarg class ColumnSettings( val contentPadding: PaddingValues = PaddingValues(0.dp), val reverseLayout: Boolean = false, val verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, val horizontalAlignment: Alignment.Horizontal = Alignment.Start, val userScrollEnabled: Boolean = true ) @Composable fun LazyColumn( modifier: Modifier = Modifier, state: LazyListState = rememberLazyListState(), dataarg args: ColumnSettings, flingBehavior: FlingBehavior = .. . , content: @Composable RowScope.() -> Unit ) { /* .. . * / } LazyColumn(reverseLayout = true) { //... }

Slide 84

Slide 84 text

LazyColumn { val lastData = searchData.last { it.id == id } val searchPanel = selectedSearchPanel() when { searchPanel is SearchPanel.NewsPanel && !searchPanel.isBlocked -> item { NewResult(lastData) } searchPanel == SearchPanel.SpeakersPanel - > item { } searchPanel == SearchPanel.TalksPanel -> item { } } }

Slide 85

Slide 85 text

/** * Returns the last element matching the given [predicate]. */ public inline fun Sequence.last(predicate: (T) -> Boolean): T { var result: T? = null for (element in this) if (predicate(element)) result = element return result ? : throw NoSuchElementException("Not found") } Find last matching element in the sequence

Slide 86

Slide 86 text

What if the predicate is '{ it == null }' /** * Returns the last element matching the given [predicate]. */ public inline fun Sequence.last(predicate: (T) -> Boolean): T { var last: T? = null var found = false for (element in this) { if (predicate(element)) { last = element found = true } } if (!found) throw NoSuchElementException("Not found") @Suppress("UNCHECKED_CAST") return last as T }

Slide 87

Slide 87 text

private object NotFound fun Sequence.last(predicate: (T) -> Boolean): T { var result: Any? = NotFound for (element in this) if (predicate(element)) result = element if (result == = NotFound) throw NoSuchElementException("Not found") return result as T } Can we do better?

Slide 88

Slide 88 text

private object NotFound fun Sequence.last(predicate: (T) -> Boolean): T { var result: Any? = NotFound for (element in this) if (predicate(element)) result = element if (result == = NotFound) throw NoSuchElementException("Not found") return result as T } Use of 'Any?' type Unchecked cast Can we do better?

Slide 89

Slide 89 text

Union types for errors private error object NotFound fun Sequence.last(predicate: (T) -> Boolean): T { var result: T | NotFound = NotFound for (element in this) if (predicate(element)) result = element if (result is NotFound) throw NoSuchElementException("Not found") return result } Union types for errors Automatic smart-cast In research

Slide 90

Slide 90 text

https://vimeo.com/747697423 val client = buildClient { firstName = "Anton" lastName = "Arhipov" twitter { handle = "@antonarhipov" } company { name = "JetBrains" city = "Tallinn" } } println("Created client is: $client")

Slide 91

Slide 91 text

class Client(var name: String? = null, var birthday: LocalDate? = null) fun buildClient(init: Client.() - > Unit): Client { var client = Client() client.init() return client } Use case: type-safe builders (a.k.a DSLs)

Slide 92

Slide 92 text

class Client(var name: String? = null, var birthday: LocalDate? = null) fun buildClient(init: Client.() - > Unit): Client { var client = Client() client.init() return client } Use case: type-safe builders DSL library

Slide 93

Slide 93 text

class Client(var name: String? = null, var birthday: LocalDate? = null) fun buildClient(init: Client.() - > Unit): Client { var client = Client() client.init() return client } Use case: type-safe builders DSL library buildClient { name = "Bob" birthday = LocalDate.of(2000, 3, 10) } User code

Slide 94

Slide 94 text

class Client(var name: String? = null, var birthday: LocalDate? = null) fun buildClient(init: Client.() - > Unit): Client { var client = Client() client.init() return client } Use case: type-safe builders DSL library buildClient { name = "Bob" birthday = LocalDate.of(2000, 3, 10) } User code Can we do better?

Slide 95

Slide 95 text

class Client(var name: String? = null, var birthday: LocalDate? = null) fun buildClient(init: Client.() - > Unit): Client { var client = Client() client.init() return client } Use case: type-safe builders DSL library buildClient { name = "Bob" birthday = 10 March 2000 } User code infix fun Int.March(year: Int) = LocalDate.of(year, Month.MARCH, this) val dob = 10 March 2000

Slide 96

Slide 96 text

class Client(var name: String? = null, var birthday: LocalDate? = null) fun buildClient(init: Client.() - > Unit): Client { var client = Client() client.init() return client } Use case: type-safe builders DSL library buildClient { name = "Bob" birthday = 10 March 2000 } User code infix fun Int.March(year: Int) = LocalDate.of(year, Month.MARCH, this) val dob = 10 March 2000 How can we restrict the scope?

Slide 97

Slide 97 text

object ClientBuilderContext context(_: ClientBuilderContext) infix fun Int.March(year: Int) = LocalDate.of(year, Month.MARCH, this) DSL library buildClient { name = "Bob" birthday = 10 March 2000 } User code val dob = 10 March 2000 Context parameters (KEEP-367)

Slide 98

Slide 98 text

DSL library buildClient { name = "Bob" birthday = 10 March 2000 } User code val dob = 10 March 2000 fun buildClient(init: context(ClientBuilderContext) Client.() - > Unit): Client = with(ClientBuilderContext()) { //. .. } object ClientBuilderContext context(_: ClientBuilderContext) infix fun Int.March(year: Int) = LocalDate.of(year, Month.MARCH, this) Context parameters (KEEP-367)

Slide 99

Slide 99 text

DSL library buildClient { name = "Bob" birthday = 10 March 2000 } User code val dob = 10 March 2000 fun buildClient(init: context(ClientBuilderContext) Client.() - > Unit): Client = with(ClientBuilderContext()) { //. .. } object ClientBuilderContext context(_: ClientBuilderContext) infix fun Int.March(year: Int) = LocalDate.of(year, Month.MARCH, this) Context parameters (KEEP-367)

Slide 100

Slide 100 text

DSL library buildClient { name = "Bob" birthday = 10 March 2000 } User code val dob = 10 March 2000 fun buildClient(init: context(ClientBuilderContext) Client.() - > Unit): Client = with(ClientBuilderContext()) { //. .. } object ClientBuilderContext context(_: ClientBuilderContext) infix fun Int.March(year: Int) = LocalDate.of(year, Month.MARCH, this) The required context is missing Required context available in this block Context parameters (KEEP-367)

Slide 101

Slide 101 text

buildClient { name = "Bob" birthday = 10 March 2000 } Loos perfect! Or isn't?...

Slide 102

Slide 102 text

buildClient { name = "Bob" // 'name' property stays uninitialized birthday = 10 March 2000 } Or... What if the user forgets to assign a property?

Slide 103

Slide 103 text

E ff ect system capabilities buildClient { name = "Bob" birthday = 10 March 2000 }

Slide 104

Slide 104 text

E ff ect system capabilities buildClient { name = "Bob" birthday = 10 March 2000 }

Slide 105

Slide 105 text

E ff ect system capabilities buildClient { name = "Bob" birthday = 10 March 2000 } In research

Slide 106

Slide 106 text

E ff ect system capabilities buildClient { name = "Bob" birthday = 10 March 2000 } fun buildClient(init: context(ClientBuilderContext) Client.() -> Unit): Client { contract { called(init@name, ONCE) called(init@birthday, ONCE) } with(ClientBuilderContext()) { // ... } } In research

Slide 107

Slide 107 text

E ff ect system capabilities buildClient { name = "Bob" birthday = 10 March 2000 } fun buildClient(init: context(ClientBuilderContext) Client.() -> Unit): Client { contract { called(init@name, ONCE) called(init@birthday, ONCE) } with(ClientBuilderContext()) { // ... } } Contract: "Ensure that the 'name' property is assigned once in the 'init' block" In research

Slide 108

Slide 108 text

Summary Kotlin 2.0: new compiler & more smart-casts More features are coming for working with data Stronger abstractions and improvements in the type system

Slide 109

Slide 109 text

https://speakerdeck.com/antonarhipov @antonarhipov https://github.com/antonarhipov