Slide 1

Slide 1 text

Kotlin Coroutines Mario Arias - Cake Solutions Ltd, Lunch & Learn March 2018 @dh44t

Slide 2

Slide 2 text

Topics About me A tiny introduction to Kotlin Function types Extension points Function composition, currying and partial application funKTionale’s Option, Either and Disjunction Future developments

Slide 3

Slide 3 text

Software Engineer at Cake Solutions 12+ years of experience with JVM technologies Spring certified trainer 7+ years with Scala 5+ years with Kotlin funKTionale Arrow (funTKtionale + Kategory) RxKotlin original developer and former team leader Spring JDBC Kotlin support

Slide 4

Slide 4 text

Co-author of Functional Kotlin March 2018

Slide 5

Slide 5 text

What is Kotlin? Statically typed programming language for modern multiplatform applications

Slide 6

Slide 6 text

Hello world package org.cakesolutions.kotlin fun main(args: Array) { println("Hello Reddish!") }

Slide 7

Slide 7 text

Features •Classes •sealed (ADT), data •Interfaces with body methods •Objects •Companion objects •Enum •Generics •Named and default parameters

Slide 8

Slide 8 text

Features II •Control structures •if else, when •do, while, for •Expressions •Null Safety •High order functions •Delegates •Extension functions

Slide 9

Slide 9 text

Null Safety package org.cakesolutions.kotlin val hello0: String = "" val hello1: String = null //Null can not be a value of a non-null type String val hello2: String? = null val reversed0: String? = hello2?.reversed() val reversed1: String = reversed0.reversed()

Slide 10

Slide 10 text

Null Safety II Any String Nothing

Slide 11

Slide 11 text

Null Safety III Any String Nothing Any? String?

Slide 12

Slide 12 text

Functions /** A function that takes 1 argument. */ public interface Function1 : Function { /** Invokes the function with the specified argument. */ public operator fun invoke(p1: P1): R }

Slide 13

Slide 13 text

Functions II fun doWithTwoNumbers(a: Int, b: Int, f: (Int, Int)  Int): Int { return f(a, b) } fun main(args: Array) { val x = doWithTwoNumbers(2, 3, { a, b  a + b }) doWithTwoNumbers(x, 3) { a, b  a * b } }

Slide 14

Slide 14 text

Functions III Create your own control structures!! fun unless(conditional: Boolean, body: ()  Unit) { if (!conditional) { body() } } fun main(args: Array) { val age: Int = //Magic here! unless(age > 18) { //Play video games all day long } }

Slide 15

Slide 15 text

Delegated property lazy is NOT a language feature but a library function val lazyValue: String by lazy { println("computed") "Hello" } fun main(args: Array) { println(lazyValue) println(lazyValue) }

Slide 16

Slide 16 text

Delegated property import kotlin.properties.Delegates class User { var name: String by Delegates.observable("") { property, oldValue, newValue  println("$oldValue  $newValue") } } fun main(args: Array) { val user = User() user.name = "first" user.name = "second" }

Slide 17

Slide 17 text

Extension functions fun String.fromReddish() = "$this, FROM Reddish" fun main(args: Array) { println("Hello World".fromReddish()) }

Slide 18

Slide 18 text

Extension functions II operator fun String.invoke(suffix: String) = "$this$suffix" fun main(args: Array) { println("This isn't a function"("")) }

Slide 19

Slide 19 text

Extension functions III fun main(args: Array) { val myList = listOf(1, 2, 3, 4) //call to method reference .map(InttoString) .map { i  i + i } .map(StringtoInt) .filter { it > 20 } // `it` is an implicit parameter println(myList) }

Slide 20

Slide 20 text

Combine all the things!!! one-line unless from Ruby!! infix fun (()  Unit).unless(condition: Boolean) { if (!condition) { this() } } fun main(args: Array) { val age: Int = //Magic { /*pay taxes*/ } unless (age < 18) }

Slide 21

Slide 21 text

Inline fun time(body: ()  T): Pair { val startTime = System.currentTimeMillis() val v = body() val endTime = System.currentTimeMillis() return v to endTime - startTime } inline fun inTime(body: ()  T): Pair { val startTime = System.currentTimeMillis() val v = body() val endTime = System.currentTimeMillis() return v to endTime - startTime }

Slide 22

Slide 22 text

Inline II fun main(args: Array) { val b = time { Thread.sleep(1000) "boring" } val i = inTime { Thread.sleep(1000) "inline" } println(b) println(i) }

Slide 23

Slide 23 text

Inline III @NotNull public static final Pair time(@NotNull final Function0 body) { Intrinsics.checkParameterIsNotNull((Object)body, "body"); final long startTime = System.currentTimeMillis(); final Object v = body.invoke(); final long endTime = System.currentTimeMillis(); return (Pair)TuplesKt.to(v, (Object)(endTime - startTime)); } @NotNull public static final Pair inTime(@NotNull final Function0 body) { Intrinsics.checkParameterIsNotNull((Object)body, "body"); final long startTime = System.currentTimeMillis(); final Object v = body.invoke(); final long endTime = System.currentTimeMillis(); return (Pair)TuplesKt.to(v, (Object)(endTime - startTime)); } Decompiled bytecode

Slide 24

Slide 24 text

Inline IV public static final void main(@NotNull final String[] args) { Intrinsics.checkParameterIsNotNull((Object)args, "args"); final Pair b = time( (kotlin.jvm.functions.Function0)_05_inlineKt$main$b._05_inlineKt$main$b$1.INSTANCE ); final long startTime$iv = System.currentTimeMillis(); Thread.sleep(1000L); final Object v$iv = "inline"; final long endTime$iv = System.currentTimeMillis(); final Pair i = TuplesKt.to(v$iv, (Object)(endTime$iv - startTime$iv)); System.out.println(b); System.out.println(i); }

Slide 25

Slide 25 text

Inline V static final class _05_inlineKt$main$b$1 extends Lambda implements Function0 { public static final _05_inlineKt$main$b$1 INSTANCE; @NotNull public final String invoke() { Thread.sleep(1000L); return "boring"; } static { _05_inlineKt$main$b$1.INSTANCE = new _05_inlineKt$main$b$1(); } }

Slide 26

Slide 26 text

Type-safe builders fun logged(body: Logger.()  T): T { val log = LoggerFactory.getLogger("builder") return log.body() } fun main(args: Array) { val i = logged { info("THIS is INFO") debug("THIS is DEBUG") error("THIS is ERROR") 1 + 2 } }

Slide 27

Slide 27 text

DSLs class FxApp : App(FxViewclass) class FxView : View() { override val root = vbox { label("Reddish App") button("Press me") } } fun main(args: Array) { Application.launch(FxAppclass.java, *args) }

Slide 28

Slide 28 text

Coroutines but first, Threads import kotlin.concurrent.thread fun main(args: Array) { val computation = thread { Thread.sleep(1000) println("World!") } print("Hello, ") computation.join() }

Slide 29

Slide 29 text

Hardware and JVM Threads a simplistic, and incomplete, explanation Core 1 T1 T2 T3 Core 2 T4 T.. TN

Slide 30

Slide 30 text

Monitoring utils VisualVM class MonitorUtil { private val scanner = Scanner(System.`in`) fun awaitEnter(){ println("Press enter to continue") scanner.nextLine() } } fun monitored(body: MonitorUtil.()  Unit) { val monitor = MonitorUtil() monitor.body() monitor.awaitEnter() }

Slide 31

Slide 31 text

1000 Threads fun main(args: Array) = monitored { awaitEnter() //VisualVM val threads = List(1000) { thread { Thread.sleep(1000) print('.') } } threads.forEach(Threadjoin) }

Slide 32

Slide 32 text

Executors fun main(args: Array) = monitored { awaitEnter() val pool = Executors.newFixedThreadPool(128) repeat(10000) { pool.submit { Thread.sleep(1000) print('.') } } pool.shutdown() pool.awaitTermination(2, TimeUnit.SECONDS) }

Slide 33

Slide 33 text

Coroutines “..a coroutine is a very light thread that runs a block of code and has a similar life cycle, but can complete with a return value or an exception. Technically, a coroutine is an instance of a suspendable computation, a computation that may suspend. Coroutines aren't bound to a particular thread and can suspend in one Thread and resume execution in a different one” From the book Functional Kotlin

Slide 34

Slide 34 text

Coroutines II Core 1 T1 T2 T3 Core 2 T4 T.. TN Coroutines

Slide 35

Slide 35 text

Coroutines III import kotlinx.coroutines.experimental.delay import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.runBlocking fun main(args: Array) = runBlocking { val job = launch { delay(1000) println("World!") } print("Hello, ") job.join() }

Slide 36

Slide 36 text

Coroutines IV Concept Description Coroutine Light thread Suspending function A function with a suspend modifier. It can suspend a coroutine without blocking the thread Suspending lambda A lambda function with a suspend modifier Coroutine builder A function that takes a suspending lambda Suspension point A point where a suspending function is invoked Continuation The state of a suspended coroutine at a suspension point

Slide 37

Slide 37 text

Coroutines V fun main(args: Array) = runBlocking { val job = launch { delay(1000) println("World!") } print("Hello, ") job.join() } Suspension point Suspension point Suspending function Coroutine builder Coroutine builder Continuation Suspending lambda Suspending lambda Suspending function

Slide 38

Slide 38 text

10000 Coroutines fun main(args: Array) = monitored { awaitEnter() runBlocking { val jobs = List(10000) { launch { delay(1000) print(".") } } jobs.forEach { it.join() } } }

Slide 39

Slide 39 text

Shared state suspend fun repeatInParallel(times: Int, block: suspend ()  Unit) { val job = launch { repeat(times) { launch(coroutineContext) { block() } } } job.join() }

Slide 40

Slide 40 text

Shared state II fun main(args: Array) = runBlocking { var counter = 0 val time = measureTimeMillis { repeatInParallel(1_000_000) { counter } } println("counter = $counter") //EPIC FAIL println("time = $time") }

Slide 41

Slide 41 text

Atomic Integer import java.util.concurrent.atomic.AtomicInteger fun main(args: Array) = runBlocking { val counter = AtomicInteger(0) val time = measureTimeMillis { repeatInParallel(1_000_000) { counter.incrementAndGet() } } println("counter = ${counter.get()}") println("time = $time") }

Slide 42

Slide 42 text

Single thread context fun main(args: Array) = runBlocking { var counter = 0 val counterContext = newSingleThreadContext("CounterContext") val time = measureTimeMillis { repeatInParallel(1_000_000) { withContext(counterContext) { counter } } } println("counter = $counter") println("time = $time") }

Slide 43

Slide 43 text

Mutex fun main(args: Array) = runBlocking { var counter = 0 val mutex = Mutex() val time = measureTimeMillis { repeatInParallel(1_000_000) { mutex.withLock { counter } } } println("counter = $counter") println("time = $time") }

Slide 44

Slide 44 text

Actors I sealed class CounterMsg object IncCounter : CounterMsg() class GetCounter(val response: CompletableDeferred) : CounterMsg() fun counterActor(start: Int) = actor { var counter = start for (msg in channel) { when (msg) { is IncCounter  counter is GetCounter  msg.response.complete(counter) } } }

Slide 45

Slide 45 text

Actors II fun main(args: Array) = runBlocking { val counterActor = counterActor(0) val time = measureTimeMillis { repeatInParallel(1_000_000) { counterActor.send(IncCounter) } } val counter = CompletableDeferred() counterActor.send(GetCounter(counter)) println("counter = ${counter.await()}") println("time = $time") }

Slide 46

Slide 46 text

Other uses import arrow.core.Some fun main(args: Array) { val a = Some(1) val b = Some(2) val c = a.flatMap { x  b.map { y  x + y } } println("c = $c") }

Slide 47

Slide 47 text

Monadic comprehension import arrow.core.Option import arrow.core.Some import arrow.core.ev import arrow.core.monad import arrow.typeclasses.binding fun main(args: Array) { val a = Some(1) val b = Some(2) val c = Option.monad().binding { val x = a.bind() val y = b.bind() x + y }.ev() println("c = $c") } continuation: flatMap continuation: map