Principles of Pragmatic Evolution
Language design is cast in stone,
but this stone is reasonably soft,
and with some effort we can reshape
it later.
Kotlin Design Team
* The real sculpture made by José Manuel Castro López
Slide 8
Slide 8 text
Principles of Pragmatic Evolution
• keeping the language modern
• comfortable updates
• feedback loop
Slide 9
Slide 9 text
KEEPing the language modern
https://github.com/Kotlin/KEEP
KEEP = Kotlin Evolution
and Enhancement Process
contains language proposals
and the corresponding discussions
Slide 10
Slide 10 text
Comfortable updates
• Deprecation warnings in advance
• Automatic migration
Slide 11
Slide 11 text
Feedback loop with the community
• Kotlin the community
• Kotlin team listens to the community
• The community members influence
the Kotlin evolution
Slide 12
Slide 12 text
Contributors from all over the world
…
Slide 13
Slide 13 text
Experimental
features
Slide 14
Slide 14 text
Experimental features
The goal: to let new features be tried
by early adopters as soon as possible
Slide 15
Slide 15 text
Experimental Language Features
• can be changed in the future
• you need to explicitly opt in at the call site
to use experimental features
compileTestKotlin {
kotlinOptions {
freeCompilerArgs += "-Xinline-classes"
}
}
Slide 16
Slide 16 text
Experimental API for Libraries
• can be publicly released as a part of the library
• may break at any moment
Slide 17
Slide 17 text
@ShinyNewAPI
class Foo {
...
}
Experimental API
@Experimental
annotation class ShinyNewAPI
You can mark your new class or function as experimental
Slide 18
Slide 18 text
Using experimental API
@UseExperimental(ShinyNewAPI::class)
fun doSomethingImportant() {
val foo = Foo()
...
}
Slide 19
Slide 19 text
• feedback loop for new features and API
Experimental: Summary
Slide 20
Slide 20 text
Inline classes
Slide 21
Slide 21 text
Inline classes
• can wrap values without additional
overhead
• currently an experimental feature
Slide 22
Slide 22 text
Duration API
• KEEP: Duration and Time Measurement API
• status: experimental in Kotlin 1.3.50
• uses inline classes
Slide 23
Slide 23 text
Refining API: trying primitive args
fun greetAfterTimeout(millis: Long)
greetAfterTimeout(2) // meaning 2 seconds?
Slide 24
Slide 24 text
Refining API: trying primitive args
fun greetAfterTimeout(millis: Long)
greetAfterTimeout(2) // meaning 2 seconds?
Too easy to make a mistake
Slide 25
Slide 25 text
Refining API: trying overloads
fun greetAfterTimeoutMillis(millis: Long)
fun greetAfterTimeoutSeconds(seconds: Long)
greetAfterTimeoutSeconds(2)
Slide 26
Slide 26 text
Refining API: trying overloads
fun greetAfterTimeoutMillis(millis: Long)
fun greetAfterTimeoutSeconds(seconds: Long)
greetAfterTimeoutSeconds(2)
Too verbose
Slide 27
Slide 27 text
Refining API: trying class
fun greetAfterTimeout(duration: Duration)
greetAfterTimeout(Duration(millis = 2000))
class Duration(val millis: Long)
Slide 28
Slide 28 text
Refining API: trying class
fun greetAfterTimeout(duration: Duration)
greetAfterTimeout(Duration(millis = 2000))
Extra object is allocated
class Duration(val millis: Long)
Slide 29
Slide 29 text
Inline classes to the rescue
inline class Duration(val value: Long)
fun greetAfterTimeout(duration: Duration)
fun greetAfterTimeout_(duration: Long)
Under the hood:
Slide 30
Slide 30 text
Inline classes to the rescue
inline class Duration(val value: Long)
fun greetAfterTimeout(duration: Duration)
fun greetAfterTimeout_(duration: Long)
Under the hood:
No extra object is allocated! ✓
Slide 31
Slide 31 text
Inline classes constraints
inline class Duration(val value: Long)
fun greetAfterTimeout(duration: Duration)
inline class can define only one val property
Slide 32
Slide 32 text
Creating Duration
fun greetAfterTimeout(duration: Duration)
greetAfterTimeout(2.seconds)
✓
Explicit units in the code
val Int.seconds
get() = toDuration(DurationUnit.SECONDS)
Slide 33
Slide 33 text
Inline classes: summary
• KEEP: inline classes
• help to improve API and avoid extra
allocations
• you can go and try it out
Slide 34
Slide 34 text
Contracts
Slide 35
Slide 35 text
Changes in standard library
fun String?.isNullOrEmpty(): Boolean {
return this == null || this.length == 0
}
Slide 36
Slide 36 text
Changes in standard library
fun String?.isNullOrEmpty(): Boolean {
return this == null || this.length == 0
}
fun String?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || this.length == 0
}
Slide 37
Slide 37 text
Changes in standard library
fun String?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || this.length == 0
}
fun String?.isNullOrEmpty(): Boolean {
return this == null || this.length == 0
}
Slide 38
Slide 38 text
val s: String? = ""
if (!s.isNullOrEmpty()) {
s.first()
}
We know something about isNullOrEmpty,
which the compiler doesn’t
Slide 39
Slide 39 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 40
Slide 40 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
if (s != null && s.isNotEmpty()) {
s.first()
}
✓
Slide 41
Slide 41 text
We know something about run,
that the compiler doesn’t
val answer: Int
run {
answer = 42
}
println(answer)
Slide 42
Slide 42 text
We know something about run,
that the compiler doesn’t
val answer: Int
run {
answer = 42
}
println(answer)
Compiler error:
Captured values initialization is forbidden
due to possible reassignment
Slide 43
Slide 43 text
Kotlin Contracts
…allow to share extra information
about code semantics with the compiler
Slide 44
Slide 44 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 45
Slide 45 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 46
Slide 46 text
inline fun run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
Contract: block lambda
will be always called once
Slide 47
Slide 47 text
inline fun run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
Contract: block lambda
will be always called once
val answer: Int
run {
answer = 42
}
✓
Slide 48
Slide 48 text
Why can’t compiler just implicitly
infer such information?
Slide 49
Slide 49 text
Why can’t compiler just implicitly
infer such information?
Because then such implicitly inferred information:
- can be implicitly changed
- can accidentally break code depending on it
Slide 50
Slide 50 text
Why can’t compiler just implicitly
infer such information?
Because then such implicitly inferred information:
- can be implicitly changed
- can accidentally break code depending on it
Slide 51
Slide 51 text
Contract = explicit statement about function behaviour
Why can’t compiler just implicitly
infer such information?
Because then such implicitly inferred information:
- can be implicitly changed
- can accidentally break code depending on it
Slide 52
Slide 52 text
Kotlin Contract
extra information by developer
&
compiler uses this information for code analysis
experimental
Slide 53
Slide 53 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 54
Slide 54 text
• handy functions (run, isEmptyOrNull)
are even more useful
• contract DSL will change
• more use-cases in the future
• you can define contracts for your own
functions
Contracts: Summary
Slide 55
Slide 55 text
Channels
Slide 56
Slide 56 text
Channels
used for synchronization
communication
between coroutines
Slide 57
Slide 57 text
“Share by communicating”
Shared
Mutable State
Share by
Communicating
Synchronization
Primitives
Communication
Primitives
Send & Receive “views” for the same channel
interface SendChannel {
suspend fun send(element: E)
fun close()
}
interface ReceiveChannel {
suspend fun receive(): E
}
interface Channel : SendChannel, ReceiveChannel
Slide 61
Slide 61 text
Send & Receive “views” for the same channel
interface SendChannel {
suspend fun send(element: E)
fun close()
}
interface ReceiveChannel {
suspend fun receive(): E
}
interface Channel : SendChannel, ReceiveChannel
Integration with RxJava
Use extension functions:
• flow.asPublisher()
• publisher.asFlow()
Slide 93
Slide 93 text
Backpressure
• Backpressure happens automatically
thanks to suspension mechanism
Slide 94
Slide 94 text
Flows: summary
• bring reactive streams to coroutines library
• currently in an experimental state
• will get stable soon
Slide 95
Slide 95 text
Multi-platform
Projects
Slide 96
Slide 96 text
fun Char.isUpperCase(): Boolean =
java.lang.Character.isUpperCase(this)
Before:
expect / actual in standard library
Slide 97
Slide 97 text
expect fun Char.isUpperCase(): Boolean
public actual fun Char.isUpperCase(): Boolean =
java.lang.Character.isUpperCase(this)
Now:
fun Char.isUpperCase(): Boolean =
java.lang.Character.isUpperCase(this)
Before:
expect / actual in standard library
Slide 98
Slide 98 text
expect fun Char.isUpperCase(): Boolean
public actual fun Char.isUpperCase(): Boolean =
java.lang.Character.isUpperCase(this)
Now:
fun Char.isUpperCase(): Boolean =
java.lang.Character.isUpperCase(this)
Before:
expect / actual in standard library
Slide 99
Slide 99 text
Multi-platform projects
Browser
Kotlin/JS
Android
Kotlin/JVM
iOS
Kotlin/Native
Server
Kotlin/JVM
common
code
Slide 100
Slide 100 text
Sharing common code
• Sharing business logic
• Keeping UI platform-dependent
• The shared part might vary
Slide 101
Slide 101 text
Common code
• you define expect declarations in the common
code and use them
• you provide different actual implementations
for different platforms
Slide 102
Slide 102 text
Time measurement example
expect fun measureTime(action: () -> Unit): Duration
Expected platform-specific API:
Expected API can be used in the common code:
measureTime {
// operation
}
Slide 103
Slide 103 text
Platform-specific Implementations
expect fun measureTime(action: () -> Unit): Duration
actual fun measureTime(action: () -> Unit): Duration {
// implementation using System.nanoTime()
}
actual fun measureTime(action: () -> Unit): Duration {
// implementation using window.performance.now()
}
actual fun measureTime(action: () -> Unit): Duration {
// implementation using std::chrono::high_resolution_clock
}
Kotlin/JVM
Kotlin/JS
Kotlin/Native
Slide 104
Slide 104 text
Common code
• can use the standard library
• can define expect declarations and use them
• can use other multi-platform libraries
Slide 105
Slide 105 text
Multi-platform libraries
• Standard library
• Ktor HTTP client
• kotlinx.serialization
• kotlinx.coroutines
• … and more
Slide 106
Slide 106 text
Many apps already in production
Going Native: How I used Kotlin Native to Port 6 years
of Android Game Code to iOS in 6 months
Shipping a Mobile Multiplatform Project on iOS &
Android
Your Multiplatform Kaptain has Arrived: iOS release is
powered by Kotlin Multiplatform
Slide 107
Slide 107 text
Multi-platform projects: summary
• a modern approach to multi-platform
development
• you can easily tune what parts you
want to be shared
• you can go and try it out
Slide 108
Slide 108 text
More about Kotlin
Slide 109
Slide 109 text
No content
Slide 110
Slide 110 text
Kotlin course at Coursera
Slide 111
Slide 111 text
Hands-on lab “Intro to coroutines & channels”
http://kotl.in/hands-on