Beyond Kotlin - Advanced features for API Makers

E02cd1d0fc5c51ac491b484a258a63a6?s=47 Arnaud GIULIANI
September 30, 2017

Beyond Kotlin - Advanced features for API Makers

Kotlin talk for experimented developers

E02cd1d0fc5c51ac491b484a258a63a6?s=128

Arnaud GIULIANI

September 30, 2017
Tweet

Transcript

  1. Beyond Kotlin Advanced features for API makers

  2. Work @ ekito Mobile & Cloud Kotlin Lover @arnogiu Arnaud

    GIULIANI medium.com/@giuliani.arnaud/ ekito.fr/people #DevFestToulouse
  3. None
  4. None
  5. Statically typed programming language for modern multiplatform applications

  6. None
  7. Kotlin on Android, now official

  8. h5ps:/ /spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring- framework-5-0 h5p:/ /www.javamagazine.mozaicreader.com/ #&pageSet=5&page=0&contentItem=0 (March/ April 2017) h5ps:/

    /www.thoughtworks.com/radar/languages-and-frameworks/kotlin
  9. h5ps:/ /zeroturnaround.com/rebellabs/developer-producNvity-report-2017-why-do-you-use-java-tools-you-use/

  10. None
  11. Many well-known companies are using Kotlin: Pinterest, Coursera, NeUlix, Uber,

    Square, Trello, Basecamp, amongst others well-known banks (such as Goldman Sachs, Wells Fargo, J.P. Morgan, Deutsche Bank, UBS, HSBC, BNP Paribas, Société Générale)
  12. None
  13. KOTLIN is not just a syntactic sugar It’s all about

    writing SAFER & BETTER APPS !
  14. val / var Null safety Class / Object Lambda Functions

    Data Class Properties & delegates Default Values Named Parameters Extension Functions InterOp Not today!
  15. None
  16. Let’s take a concrete context

  17. // A Bean Definition data class BeanDefinition(val name: String, val

    clazz: KClass<*>) like dependency injection An advanced API development use case Let’s take
  18. Before, we need some APIs ⚒

  19. Writing Lambda APIs

  20. // A Bean definition val beanDef: BeanDefinition? // let beanDef?.let

    { println("bean name is '${it.name}'") } // let & assign value val complexName: String? = beanDef?.let { "name : ${it.name} & class ${it.clazz}" } Safely executing with let // A Bean definition val beanDef: BeanDefinition? // let beanDef?.let { println("bean name is '${it.name}'") }
  21. // takeIf (validate predicate) val bean = beanDef?.takeIf { it.name.isNotEmpty()

    } val bean = beanDef?.takeUnless { it.name.isNullOrEmpty() } // A Bean definition val beanDef: BeanDefinition? = ... // Guard like expression val bean: BeanDefinition = beanDef?.takeIf { it.name.isNotEmpty() } ?: error("bean name is empty") // Guard like expression val bean: BeanDefinition = beanDef?.takeIf { it.name.isNotEmpty() } ?: error("bean name is empty") val bean: BeanDefinition = beanDef?.takeIf { it.name.isNotEmpty() } ?: return // takeIf (validate predicate) val bean = beanDef?.takeIf { it.name.isNotEmpty() } val bean = beanDef?.takeUnless { it.name.isNullOrEmpty() } takeIf & takeUnless // A Bean definition val beanDef: BeanDefinition? = ...
  22. let ~ run -> return last value also ~ apply

    -> return itself with() -> function & return last value it let ~ run -> return last value also ~ apply -> return itself with() -> function & return last value this
  23. Encapsulate behavior for a target object

  24. Lambda function Receiver Type fun T.function( (T) -> R) fun

    T.function( T.() -> R) Writing encapsulation public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this } public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this } public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this } public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this } public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this } public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this } public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this } public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
  25. SAM Conversion public class JavaBeanDefinition { String name; Class clazz;

    public JavaBeanDefinition(String name, Class clazz) {...} public void postInit(JavaInitializingBean initBean){ // Register post init } } public interface JavaInitializingBean { void onInitDone(); } val clazz = MyService::class.java val javaBean = JavaBeanDefinition(clazz.simpleName,clazz) public class JavaBeanDefinition { String name; Class clazz; public JavaBeanDefinition(String name, Class clazz) {...} public void postInit(JavaInitializingBean initBean){ // Register post init } } public interface JavaInitializingBean { void onInitDone(); } val clazz = MyService::class.java val javaBean = JavaBeanDefinition(clazz.simpleName,clazz) javaBean.postInit { println("bean has been defined ! ") } JAVA JAVA
  26. The functional way // A Bean definition with post init

    data class BeanDefinition(val name: String, val clazz: KClass<*>) { fun postInit(initializingBean: InitializingBean) { // register post init ... } } // A Bean definition with post init data class BeanDefinition(val name: String, val clazz: KClass<*>) { fun postInit(initializingBean: () -> Unit) { // register post init ... } } val clazz = MyService::class val bean = BeanDefinition(clazz.java.simpleName, clazz) val clazz = MyService::class val bean = BeanDefinition(clazz.java.simpleName, clazz) bean.postInit { println("bean has been defined ! ") }
  27. Types

  28. Types Hierarchy

  29. Types Hierarchy (again) ? ?

  30. Dealing with generics sealed class BeanDefinition(val name : String, val

    clazz : KClass<*>) class Singleton(n : String, c : KClass<*>) : BeanDefinition(n,c) class Factory(n : String, c : KClass<*>) : BeanDefinition(n,c) fun <T> registerBean(def: T) { //... } // Limit with Bounds fun <T : BeanDefinition> registerBean(def: T) { //... }
  31. Dealing with generics sealed class BeanDefinition(val name : String, val

    clazz : KClass<*>) class Singleton(n : String, c : KClass<*>) : BeanDefinition(n,c) class Factory(n : String, c : KClass<*>) : BeanDefinition(n,c) fun <T> registerBean(def: T) { //... } class BeanProvider<T : BeanDefinition> { } // Limit with Bounds fun <T : BeanDefinition> registerBean(def: T) { //... } // Limit with Bounds fun <T : BeanDefinition> registerBean(def: T) { //... } sealed class BeanDefinition(val name : String, val clazz : KClass<*>) class Singleton(n : String, c : KClass<*>) : BeanDefinition(n,c) class Factory(n : String, c : KClass<*>) : BeanDefinition(n,c)
  32. Reified Types fun declareBean(name :String, clazz : KClass<*>){ val bean

    = BeanDefinition(name,clazz) }
  33. Reified Types fun declareBean(name :String, clazz : KClass<*>){ val bean

    = BeanDefinition(name,clazz) } fun <T> declareBean(name :String, clazz : T){ val bean = BeanDefinition(name,???) } fun <T> declareBean(name :String, clazz : T){ // Capture Type parameter class val clazz = T::class val bean = BeanDefinition(name,clazz) }
  34. Reified Types fun declareBean(name :String, clazz : KClass<*>){ val bean

    = BeanDefinition(name,clazz) } fun <T> declareBean(name :String, clazz : T){ val bean = BeanDefinition(name,???) } fun <T> declareBean(name :String, clazz : T){ // Capture Type parameter class val clazz = T::class val bean = BeanDefinition(name,clazz) } inline fun <reified T> declareBean(name :String, clazz : T){ // Capture Type parameter class val clazz = T::class val bean = BeanDefinition(name,clazz) }
  35. Reified Types fun declareBean(name :String, clazz : KClass<*>){ val bean

    = BeanDefinition(name,clazz) } fun <T> declareBean(name :String, clazz : T){ val bean = BeanDefinition(name,???) } fun <T> declareBean(name :String, clazz : T){ // Capture Type parameter class val clazz = T::class val bean = BeanDefinition(name,clazz) } inline fun <reified T> declareBean(name :String, clazz : T){ // Capture Type parameter class val clazz = T::class val bean = BeanDefinition(name,clazz) } inline fun <reified T> declareBean(){ // Capture Type parameter class val clazz = T::class val name = clazz.simpleName ?: "" val bean = BeanDefinition(name,clazz) }
  36. Reified Types fun declareBean(name :String, clazz : KClass<*>){ val bean

    = BeanDefinition(name,clazz) } fun <T> declareBean(name :String, clazz : T){ val bean = BeanDefinition(name,???) } fun <T> declareBean(name :String, clazz : T){ // Capture Type parameter class val clazz = T::class val bean = BeanDefinition(name,clazz) } inline fun <reified T> declareBean(name :String, clazz : T){ // Capture Type parameter class val clazz = T::class val bean = BeanDefinition(name,clazz) } inline fun <reified T> declareBean(){ // Capture Type parameter class val clazz = T::class val name = clazz.simpleName ?: "" val bean = BeanDefinition(name,clazz) } inline fun <reified T> declareBean(){ // Capture Type parameter class val clazz = T::class val name = clazz.simpleName ?: "" val bean = BeanDefinition(name,clazz) } declareBean<MyService>()
  37. Type Aliases typealias BeanList = List<BeanDefinition> typealias BeanList = List<BeanDefinition>

    val list : BeanList = listOf()
  38. Type Aliases typealias BeanList = List<BeanDefinition> typealias BeanList = List<BeanDefinition>

    val list : BeanList = listOf() typealias BeanValidator = (BeanDefinition) -> Boolean typealias BeanList = List<BeanDefinition> val list : BeanList = listOf() typealias BeanValidator = (BeanDefinition) -> Boolean val bv : BeanValidator = { def -> def.name.isNotEmpty()}
  39. Type Aliases typealias BeanList = List<BeanDefinition> typealias BeanList = List<BeanDefinition>

    val list : BeanList = listOf() typealias BeanValidator = (BeanDefinition) -> Boolean typealias BeanList = List<BeanDefinition> val list : BeanList = listOf() typealias BeanValidator = (BeanDefinition) -> Boolean val bv : BeanValidator = { def -> def.name.isNotEmpty()} typealias BeanValidator = (BeanDefinition) -> Boolean val bv : BeanValidator = { def -> def.name.isNotEmpty()} // A Bean definition with post init data class BeanDefinition(val name: String, val clazz: KClass<*>) { fun validate(validator: BeanValidator) : Boolean = validator(this) } // A Bean definition with post init data class BeanDefinition(val name: String, val clazz: KClass<*>) { fun validate(validator: BeanValidator) : Boolean = validator(this) }
  40. Type Aliases typealias BeanList = List<BeanDefinition> typealias BeanList = List<BeanDefinition>

    val list : BeanList = listOf() typealias BeanValidator = (BeanDefinition) -> Boolean typealias BeanList = List<BeanDefinition> val list : BeanList = listOf() typealias BeanValidator = (BeanDefinition) -> Boolean val bv : BeanValidator = { def -> def.name.isNotEmpty()} typealias BeanValidator = (BeanDefinition) -> Boolean val bv : BeanValidator = { def -> def.name.isNotEmpty()} // A Bean definition with post init data class BeanDefinition(val name: String, val clazz: KClass<*>) { fun validate(validator: BeanValidator) : Boolean = validator(this) } // A Bean definition with post init data class BeanDefinition(val name: String, val clazz: KClass<*>) { fun validate(validator: BeanValidator) : Boolean = validator(this) } // A Bean definition with post init data class BeanDefinition(val name: String, val clazz: KClass<*>) { fun validate(validator: BeanValidator) : Boolean = validator(this) }
  41. None
  42. Ready to write our API

  43. Making clean syntax StringUtil.capitalize(s) s.capitalize() Extension FuncNon 1.to("one") 1 to

    "one" Infix call set.add(2) set += 2 Operator overloading map.get("key") map["key"] Get method convenNon StringUtil.capitalize(s) 1.to("one") set.add(2) map.get("key")
  44. file.use({f -> f.read()}) file.use {f -> f.read()} Lambda outside parenthesis

    sb.append("yes") sb.append("no") with (sb){ append("yes") append("no") } Lambda with receiver file.use({f -> f.read()}) sb.append("yes") sb.append("no") Making clean syntax
  45. API or DSL?

  46. DSL - a small set of features - focus on

    a particular task declarative API - a set of functions and procedures - for creation of applications imperative => internal DSL
  47. Dependency injection DSL

  48. provide { MyService() } Builder function Type reference () ->

    MyService MyService::class fun provide( definition : () -> T )
  49. provide { MyServiceA() } declareContext { } provide { MyServiceB(

    ? ) } provide { MyServiceC( ? , ?) } data class MyServiceA() data class MyServiceB(val a : MyServiceA) data class MyServiceC(val a : MyServiceA, val b : MyServiceB) provide { MyServiceC(get<MyServiceA>(),get<MyServiceB>()) } provide { MyServiceB(get<MyServiceA>()) }
  50. fun <T> provide(definition: () -> T) { } data class

    BeanDefinition(val name: String, val clazz: KClass<*>)
  51. fun <T> provide(definition: () -> T) { val clazz =

    T::class val name = clazz.java.simpleName val bean = BeanDefinition(name,clazz) } data class BeanDefinition(val name: String, val clazz: KClass<*>)
  52. inline fun <reified T> provide(definition: () -> T) { val

    clazz = T::class val name = clazz.java.simpleName val bean = BeanDefinition(definition, name, clazz) } data class BeanDefinition(val name: String, val clazz: KClass<*>)
  53. inline fun <reified T> provide(definition: () -> T) { val

    clazz = T::class val name = clazz.java.simpleName val bean = BeanDefinition(definition, name, clazz) } data class BeanDefinition<T>(val definition: () -> T, val name : String, val clazz : KClass<*>)
  54. inline fun <reified T> provide(noinline definition: () -> T) {

    val clazz = T::class val name = clazz.java.simpleName val bean = BeanDefinition(definition, name, clazz) } data class BeanDefinition<T>(val definition: () -> T, val name : String, val clazz : KClass<*>)
  55. class Context { inline fun <reified T> provide(noinline definition: ()

    -> T) { val clazz = T::class val name = clazz.java.simpleName val bean = BeanDefinition(definition, name, clazz) } } data class BeanDefinition<T>(val definition: () -> T, val name : String, val clazz : KClass<*>)
  56. class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified

    T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } } data class BeanDefinition<T>(val definition: () -> T, val name : String, val clazz : KClass<*>)
  57. class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified

    T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } } data class BeanDefinition<T>(val definition: () -> T, val name : String, val clazz : KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init)
  58. class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified

    T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } } data class BeanDefinition<T>(val definition: () -> T, val name : String, val clazz : KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init)
  59. class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified

    T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } } data class BeanDefinition<T>(val definition: () -> T, val name : String, val clazz : KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init)
  60. declareContext { } provide { MyServiceB( ? ) } provide

    { MyServiceC( ? , ?) } data class MyServiceA() data class MyServiceB(val a : MyServiceA) data class MyServiceC(val a : MyServiceA, val b : MyServiceB) provide { MyServiceA() } provide { MyServiceC(get<MyServiceA>(),get<MyServiceB>()) } provide { MyServiceB(get<MyServiceA>()) }
  61. class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified

    T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } } data class BeanDefinition<T>(val definition: () -> T, val name: String, val clazz: KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init)
  62. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init) class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } } var instances = HashMap<KClass<*>,Any>()
  63. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init) class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } fun <T> get() : T{} } var instances = HashMap<KClass<*>,Any>()
  64. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init) class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } fun <T> get() : T = instances[T::class] } var instances = HashMap<KClass<*>,Any>()
  65. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init) class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } inline fun <reified T> get() : T = instances[T::class] } var instances = HashMap<KClass<*>,Any>()
  66. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init) class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } inline fun <reified T> get() : T = instances[T::class] as T } var instances = HashMap<KClass<*>,Any>()
  67. provide { MyServiceA() } declareContext { } provide { MyServiceB(

    ? ) } provide { MyServiceC( ? , ?) } data class MyServiceA() : MyServiceA data class MyServiceB(val a : MyServiceA) data class MyServiceC(val a : MyServiceA, val b : MyServiceB) provide { MyServiceC(get<MyInterface>(),get<MyServiceB>()) } provide { MyServiceB(get<MyServiceA>()) }
  68. provide { MyServiceA() } declareContext { } provide { MyServiceB(

    ? ) } provide { MyServiceC( ? , ?) } data class MyServiceA() : MyServiceA data class MyServiceB(val a : MyServiceA) data class MyServiceC(val a : MyServiceA, val b : MyServiceB) provide { MyServiceC(get<MyInterface>(),get<MyServiceB>()) } provide { MyServiceB(get<MyServiceA>()) } Lazy evaluated by nature!
  69. provide { MyServiceA() } declareContext { } data class MyServiceA()

    : MyServiceA data class MyServiceB(val a : MyServiceA) data class MyServiceC(val a : MyServiceA, val b : MyServiceB) provide { MyServiceC(get(),get()) } provide { MyServiceB(get()) }
  70. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init) class Context { var definitions = listOf<BeanDefinition<*>>() inline fun <reified T> provide(noinline definition: () -> T) { val clazz = T::class val name = clazz.java.simpleName definitions += BeanDefinition(definition, name, clazz) } inline fun <reified T> get() : T = instances[T::class] as T } var instances = HashMap<KClass<*>,Any>()
  71. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) fun declareContext(init: Context.() -> Unit) = Context().apply(init) class Context {...} var instances = HashMap<KClass<*>,Any>()
  72. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) abstract class Module { abstract fun context() : Context } class Context {...} var instances = HashMap<KClass<*>,Any>()
  73. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) abstract class Module { abstract fun context() : Context fun declareContext(init: Context.() -> Unit) = Context().apply(init) } class Context {...} var instances = HashMap<KClass<*>,Any>()
  74. class SimpleModule : Module() { override fun context() = declareContext

    { provide { ServiceA() } provide { ServiceB(get()) } provide { ServiceC(get(), get()) } } } data class MyServiceA() data class MyServiceB(val a : MyServiceA) data class MyServiceC(val a : MyServiceA, val b : MyServiceB) Entirely declarative
  75. Dependency resolution API

  76. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) abstract class Module { abstract fun context() : Context fun declareContext(init: Context.() -> Unit) = Context().apply(init) } class Context {...} var instances = HashMap<KClass<*>,Any>()
  77. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) class Context {...} abstract class Module { abstract fun context() : Context fun declareContext(init: Context.() -> Unit) = Context().apply(init) } class CoreContext{ var instances = HashMap<KClass<*>,Any>() var definitions = listOf<BeanDefinition<*>>() }
  78. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) class Context {...} abstract class Module(){ lateinit var coreContext: CoreContext abstract fun context() : Context fun declareContext(init: Context.() -> Unit) = Context(coreContext).apply(init) } class CoreContext{ var instances = HashMap<KClass<*>,Any>() var definitions = listOf<BeanDefinition<*>>() }
  79. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) class Context {...} abstract class Module(){ lateinit var coreContext: CoreContext abstract fun context() : Context fun declareContext(init: Context.() -> Unit) = Context(coreContext).apply(init) } class CoreContext{ var instances = HashMap<KClass<*>,Any>() var definitions = listOf<BeanDefinition<*>>() }
  80. data class BeanDefinition<T>(val definition: () -> T, val name: String,

    val clazz: KClass<*>) class Context(val coreContext: CoreContext) { ... } abstract class Module(){ lateinit var coreContext: CoreContext abstract fun context() : Context fun declareContext(init: Context.() -> Unit) = Context(coreContext).apply(init) } class CoreContext{ var instances = HashMap<KClass<*>,Any>() var definitions = listOf<BeanDefinition<*>>() }
  81. class Context(val coreContext: CoreContext) { //... inline fun <reified T>

    get() : T = instances[T::class] }
  82. class Context(val coreContext: CoreContext) { //... inline fun <reified T>

    get() : T = coreContext.inject() }
  83. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() fun <T> inject(): T {} }
  84. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() fun <T> inject(): T { val clazz = T::class } }
  85. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class } }
  86. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class // found one ? val foundInstance: T? = instances[clazz] as? T } }
  87. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class // found one ? val foundInstance: T? = instances[clazz] as? T // create one ? val createdInstance: T? = if (foundInstance == null) { definitions.firstOrNull { it.clazz == clazz }?.let { it.definition.invoke() as? T? } } else null } }
  88. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class // found one ? val foundInstance: T? = instances[clazz] as? T // create one ? val createdInstance: T? = if (foundInstance == null) { definitions.firstOrNull { it.clazz == clazz }?.let { it.definition.invoke() as? T? } } else null } }
  89. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class // found one ? val foundInstance: T? = instances[clazz] as? T // create one ? val createdInstance: T? = if (foundInstance == null) { definitions.firstOrNull { it.clazz == clazz }?.let { it.definition.invoke() as? T? } } else null } }
  90. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class // found one ? val foundInstance: T? = instances[clazz] as? T // create one ? val createdInstance: T? = if (foundInstance == null) { definitions.firstOrNull { it.clazz == clazz }?.let { it.definition.invoke() as? T? } } else null } }
  91. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class // found one ? val foundInstance: T? = instances[clazz] as? T // create one ? val createdInstance: T? = if (foundInstance == null) { definitions.firstOrNull { it.clazz == clazz }?.let { it.definition.invoke() as? T? } } else null // Got it val instance: T = (foundInstance ?: createdInstance) ?: error("Bean $clazz not found") } }
  92. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class // found one ? val foundInstance: T? = instances[clazz] as? T // create one ? val createdInstance: T? = if (foundInstance == null) { definitions.firstOrNull { it.clazz == clazz }?.let { it.definition.invoke() as? T? } } else null // Got it val instance: T = (foundInstance ?: createdInstance) ?: error("Bean $clazz not found ») // Save it if (createdInstance != null && foundInstance == null) { instances[clazz] = createdInstance as Any } } }
  93. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class // found one ? val foundInstance: T? = instances[clazz] as? T // create one ? val createdInstance: T? = if (foundInstance == null) { definitions.firstOrNull { it.clazz == clazz }?.let { it.definition.invoke() as? T? } } else null // Got it val instance: T = (foundInstance ?: createdInstance) ?: error("Bean $clazz not found") // Save it if (createdInstance != null && foundInstance == null) { instances[clazz] = createdInstance as Any } return instance } }
  94. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() fun <T : Module> build(module: T) {} inline fun <reified T> inject(): T {} }
  95. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() fun <T : Module> build(module: T) { module.coreContext = this } inline fun <reified T> inject(): T {} }
  96. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() fun <T : Module> build(module: T) { module.coreContext = this definitions += module.context().definitions } inline fun <reified T> inject(): T {} }
  97. data class MyServiceA() data class MyServiceB(val a : MyServiceA) data

    class MyServiceC(val a : MyServiceA, val b : MyServiceB) class SimpleModule : Module() { override fun context() = declareContext { provide { ServiceA() } provide { ServiceB(get()) } provide { ServiceC(get(), get()) } } } Let’s use it
  98. val ctx = CoreContext() ctx.build(SimpleModule()) data class MyServiceA() data class

    MyServiceB(val a : MyServiceA) data class MyServiceC(val a : MyServiceA, val b : MyServiceB) class SimpleModule : Module() { override fun context() = declareContext { provide { ServiceA() } provide { ServiceB(get()) } provide { ServiceC(get(), get()) } } }
  99. val ctx = CoreContext() ctx.build(SimpleModule()) val serviceB = ctx.inject<MyServiceB>() data

    class MyServiceA() data class MyServiceB(val a : MyServiceA) data class MyServiceC(val a : MyServiceA, val b : MyServiceB) class SimpleModule : Module() { override fun context() = declareContext { provide { ServiceA() } provide { ServiceB(get()) } provide { ServiceC(get(), get()) } } }
  100. No code generation No annotation No introspection No proxy

  101. Reflection -> Kclass, KProperty, KFunction … -> Java => Extra

    lib And also … Extra Binding Lazy Inject
  102. None
  103. https:/ /github.com/Ekito/koin

  104. Collections & Concurrency

  105. Collections

  106. KEEP immutable collections - « real » immutable collections -

    avoid java backed collections New in 1.1 - Array-Like instantiation - onEach() - minOf/maxOf - groupingBy() - Map : minus(), getValue() Collections
  107. Stream API (Kotlin on Java 8+) - toStream() Collection to

    Sequences (Pure Kotlin or Java 6/7) - asSequence() Lazy Collections
  108. sequence.map {…} .filter {…} .toList() Intermediate operations terminal operation sequence.map

    {…} .filter {…} .toList() sequence.map {…} .filter {…} .toList()
  109. Async Programming

  110. ⚠ Experimental feature ⚠

  111. kotlinx.coroutines

  112. Kotlin >= 1.1.4 Gradle: compile ‘org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18’ Remove warnings: kotlin {

    experimental { coroutines "enable" } }
  113. Suspend - keyword, mark function as « suspending function »

    Coroutines - API for computations that can be suspended without blocking a thread - launched with coroutine builder (light-weight threads)
  114. WELCOME TO THE ASYNC WORLD

  115. @Test fun test() = runBlocking { val jobs = List(100_000)

    { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } }
  116. @Test fun test() = runBlocking { val jobs = List(100_000)

    { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } }
  117. @Test fun test() = runBlocking { val jobs = List(100_000)

    { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } }
  118. @Test fun test() = runBlocking { val jobs = List(100_000)

    { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } }
  119. @Test fun test() = runBlocking { val jobs = List(100_000)

    { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } }
  120. @Test fun test() = runBlocking { val jobs = List(100_000)

    { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } @Test fun test() = runBlocking { val main = measureTimeMillis { val jobs = List(100_000) { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } } println("\ndone in $main") }
  121. @Test fun test() = runBlocking { val main = measureTimeMillis

    { val jobs = List(100_000) { launch(CommonPool) { doSomething() } } jobs.forEach { it.join() } } println("\ndone in $main") } suspend fun doSomething() { delay(1000L) print(".") }
  122. suspend fun delay(time: Long, unit: TimeUnit = TimeUnit.MILLISECONDS) {…} suspend

    fun delay(time: Long, unit: TimeUnit = TimeUnit.MILLISECONDS) public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {…}
  123. Kotlin coroutines ~ 1s Java ForkJoin ~ 15s

  124. Dispatching jobs

  125. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  126. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  127. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  128. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  129. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  130. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  131. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  132. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  133. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 } @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  134. @Test
 fun testWeather() = runBlocking {
 log("starting ...")
 val ws

    = retrofitWS("https://weather-api.herokuapp.com/")
 try {
 val location = asyncGeocode(ws).await() ?: error("No location :(")
 val weather = asyncWeather(location, ws).await()
 log("got weather : $weather")
 } catch (e: Exception) {
 System.err.println("Error is $e")
 }
 log("finished !")
 }
 
 private fun asyncWeather(location: Location, ws: BlockingWeatherWS): Deferred<Weather> = async(CommonPool) {
 log("get weather for $location")
 ws.weather(location.lat, location.lng, "EN").execute().body()
 }
 
 private fun asyncGeocode(ws: BlockingWeatherWS): Deferred<Location?> = async(CommonPool) {
 log("get location")
 ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 }
  135. Data streaming with channels

  136. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  137. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  138. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  139. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  140. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  141. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  142. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  143. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  144. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  145. @Test
 fun testWeather() = runBlocking {
 val ws = retrofitWS("https://my-weather-api.herokuapp.com/")


    val time = measureTimeMillis {
 val location = channelLocation(ws)
 val weather = channelWeather(location, ws)
 weather.consumeEach { w ->
 log("got weather : $w")
 }
 location.cancel()
 weather.cancel()
 }
 log("\ndone in $time")
 }
 
 private fun channelWeather(locationChannel: ProducerJob<Location>, ws: BlockingWeatherWS) = produce<Forecastday_>(CommonPool) {
 log("get weather for $locationChannel")
 locationChannel.consumeEach { location ->
 val list = ws.weather(location.lat, location.lng, "EN").execute().body().forecast?.simpleforecast?.forecastday?.take(4).orEmpty()
 list.forEach { send(it) }
 }
 }
 
 private fun channelLocation(ws: BlockingWeatherWS) = produce<Location>(CommonPool) {
 log("get location")
 val location = ws.geocode("Toulouse,fr").execute().body().results.first().geometry?.location
 location?.let {
 send(location)
 }
 }
  146. Coroutines - builder (runBlocking, async, launch …) - primitives (delay,

    measureTime, job…) - communication (deferred, channel, selector, actor …) => Reactive world (RxJava …) => UI (JavaFX, Android …) => Testing (?)
  147. Further reading https:/ /github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md https:/ /github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md https:/ /github.com/Kotlin/anko/wiki/Anko-Coroutines https:/ /akarnokd.blogspot.fr/2017/09/rxjava-vs-kotlin-coroutines-quick-look.html?m=1

    https:/ /github.com/Kotlin/kotlinx.coroutines/blob/master/reactive/coroutines- guide-reactive.md Coroutines vs RxJava
  148. Beyond Kotlin Playground https:/ /github.com/Ekito/beyond-kotlin-playground

  149. What’s next?

  150. On the road to Kotlin 1.2 https:/ /blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/ https:/ /blog.jetbrains.com/kotlin/2017/06/early-access-program-for-

    kotlin-1-2-has-been-started/
  151. Others stuffs in 1.1 https:/ /kotlinlang.org/docs/reference/whatsnew11.html Gradle Kotlin Scripts https:/

    /github.com/gradle/kotlin-dsl Kotlin Native https:/ /github.com/JetBrains/kotlin-native
  152. Thank you :)