Novinky z KotlinConf 2018


Leoš Přikryl
@leos_prikryl

commity.cz, GDG Jihlava

Statistics

KotlinConf 2018

  • 3 - 5 October 2018
  • Amsterdam
  • 2nd year
  • 60 talks
  • 1300 attendees

Kotlin Developers

Github

Android & Server

  • Android
    • 4x more apps since 2017
    • 1 in 4 apps out of top 1000
  • Server
    • 2x more monthly active developers

KotlinConf Demographics

News

Kotlin 1.3

Coroutines

  • computations that can be suspended without blocking a thread
  • light-weight threads
  • asynchronous, non-blocking processing

Coroutines

  • generic low-level solution
  • work with any async concept - callbacks, futures, async/await
  • support common implementationsJDK8 CompletableFuture, Guava ListenableFuture, Google Play Services Task, Reactive Streams, RxJava, JavaFX, Swing, ...
  • cross-platform (JavaScript, Kotlin/Native)

Asynchronous code

fun postItem(item: Item) {
    val token = login()
    val post = submitPost(token, item)
    processPost(post)
}

fun login(): Token {
    // a blocking code
    return token
}

Asynchronous code - callbacks

fun postItem(item: Item) {
    loginAsync { token ->
        submitPostAsync(token, item) { post ->
            processPost(post)
        }
    }
}

fun loginAsync(callback: () -> Token) {
    // make request and return immediately
    // arrange callback to be invoked later
}

Asynchronous code - coroutines

suspend fun postItem(item: Item) {
    val token = login()
    val post = submitPost(token, item)
    processPost(post)
}

suspend fun login(): Token {

}
  • suspend is the only new keyword
  • suspending functions can only be called from coroutines

Running coroutines

GlobalScope.launch {
    syncData()
}
val deferredData : Deferred<Data> = GlobalScope.async {
    retrieveData()
}

Cancellable

val job = GlobalScope.launch {
    syncData()
}
job.cancel();

Standard exception handling

try {
    GlobalScope.launch {
        syncData()
    }
} catch (e: DataSynchronizationException) {
    //handle exception
}

Sequential by default

suspend fun loadImage(url: String): Image {
    // blocking load
}

suspend fun getCombinedImage(url1: String, url2: String): Image {
    val image1 = loadImage(url1)
    val image2 = loadImage(url2)
    return combineImages(image1, image2)
}

Parallel processing

suspend fun loadImage(url: String): Image {
    // blocking load
}

suspend fun getCombinedImage(url1: String, url2: String): Image {
    val image1 = GlobalScope.async { loadImage(url1) }
    val image2 = GlobalScope.async { loadImage(url2) }
    return combineImages(image1.await(), image2.await())
}

Structured concurrency

suspend fun loadImage(url: String): Image {
    // blocking load
}

suspend fun getCombinedImage(url1: String, url2: String): Image {
    return coroutineScope {
        val image1 = async { loadImage(url1) }
        val image2 = async { loadImage(url2) }
        combineImages(image1.await(), image2.await())
    }
}

Coroutines - sequence

fun fibonacci() = sequence {
    var terms = Pair(0, 1)

    // this sequence is infinite
    while (true) {
        yield(terms.first)
        terms = Pair(terms.second, terms.first + terms.second)
    }
}

println(fibonacci().take(10).toList()) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Contracts - return value

val s: String? = getString()

if (s != null) {
    println(s.length) // smart cast to String
}

Contracts - return value

Before Kotlin 1.3

val s: String? = getString()

if (!s.isNullOrEmpty()) {
    println(s!!.length) // cannot smart cast
}

Contracts - return value

Kotlin 1.3

val s: String? = getString()

if (!s.isNullOrEmpty()) {
    println(s.length) // smart cast to String
}
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.length == 0
}

Contracts - calls in place

val lock = Object()

val x: Int
synchronized(lock) {
    x = 42
}
println(x)
public actual inline fun <R> synchronized(lock: Any, block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    //...
}

Contracts

  • in stdlib
  • custom contracts - experimental
  • automatic check that the contract is correct
  • must be explicit

Unsigned numbers

  • UByte, UShort, UInt, ULong
  • experimental feature

Inline classes

inline class UInt(val data: Int)
  • experimental feature

Parameterless main

fun main() {
    println("Hello, world!")
}

New methods in standard library

val maybeCollection: Collection? = calculateCollection()
if (maybeCollection.isNullOrEmpty()) {
    //...
}
fun makeNullableSequence(): Sequence? = ...
val nonNullSequence: Sequence = makeNullableSequence().orEmpty()
val listOfFeatures: List = ...
println(listOfFeatures.ifEmpty { listOf("DefaultFeature1", "DefaultFeature2") })
val userProvidedValue: String = ...
println(userProvidedValue.ifBlank { "N/A" })

Tooling

Gradle Kotlin DSL 1.0

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.3.10"
}

group = "cz.commity.kotlin"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib-jdk8"))
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1")
    compile("org.jetbrains.kotlin:kotlin-scripting-jvm")
}

tasks.withType {
    kotlinOptions.jvmTarget = "1.8"
}

Progressive compiler mode

compileKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xprogressive"]
    }
}

Kotlin coding conventions

Scratch files

Revamped Kotlin Playground

Multiplatform

Kotlin/Native

  • is now beta
  • executable for all common platforms
    Linux, Windows, MacOS, Android, iOS, WebAssembly
  • static or dynamic C or C++ library
  • Apple framework for Swift or Objective-C
  • use existing C, Swift, Objective-C libraries

Multiplatform

Multiplatform

enum class LogLevel {
    DEBUG, WARN, ERROR
}

internal expect fun writeLogMessage(message: String, logLevel: LogLevel)

fun logDebug(message: String) = writeLogMessage(message, LogLevel.DEBUG)
fun logWarn(message: String) = writeLogMessage(message, LogLevel.WARN)
fun logError(message: String) = writeLogMessage(message, LogLevel.ERROR)
internal actual fun writeLogMessage(message: String, logLevel: LogLevel) {
    println("[$logLevel]: $message")
}
internal actual fun writeLogMessage(message: String, logLevel: LogLevel) {
    when (logLevel) {
        LogLevel.DEBUG -> console.log(message)
        LogLevel.WARN -> console.warn(message)
        LogLevel.ERROR -> console.error(message)
    }
}

Ktor

  • ktor.io
  • 1.0 beta
  • asynchronous framework for connected applications
  • server & client

Future

Kotlin Foundation

  • kotl.in/foundation
  • JetBrains + Google
  • Development
  • Trademarks
  • Lead Language Designer (Andrey Breslav)
  • Incompatible changes

Kotlin 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

Kotlin Evolution

  • Keeping the language modern
  • Comfortable updates
  • Feedback loop - Experimental features

Backward incompatible changes

  • Deprecation warnings
  • Migrate in advance (automatically)
  • Compatibility mode (at least one version back)

Questions?