Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Beyond Kotlin - Advanced features for API Makers

Avatar for Arnaud GIULIANI Arnaud GIULIANI
September 30, 2017

Beyond Kotlin - Advanced features for API Makers

Kotlin talk for experimented developers

Avatar for Arnaud GIULIANI

Arnaud GIULIANI

September 30, 2017
Tweet

More Decks by Arnaud GIULIANI

Other Decks in Technology

Transcript

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

    GIULIANI medium.com/@giuliani.arnaud/ ekito.fr/people #DevFestToulouse
  2. 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)
  3. val / var Null safety Class / Object Lambda Functions

    Data Class Properties & delegates Default Values Named Parameters Extension Functions InterOp Not today!
  4. // A Bean Definition data class BeanDefinition(val name: String, val

    clazz: KClass<*>) like dependency injection An advanced API development use case Let’s take
  5. // 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}'") }
  6. // 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? = ...
  7. 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
  8. 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 }
  9. 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
  10. 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 ! ") }
  11. 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) { //... }
  12. 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)
  13. 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) }
  14. 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) }
  15. 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) }
  16. 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>()
  17. 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()}
  18. 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) }
  19. 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) }
  20. 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")
  21. 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
  22. 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
  23. provide { MyService() } Builder function Type reference () ->

    MyService MyService::class fun provide( definition : () -> T )
  24. 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>()) }
  25. fun <T> provide(definition: () -> T) { } data class

    BeanDefinition(val name: String, val clazz: KClass<*>)
  26. 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<*>)
  27. 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<*>)
  28. 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<*>)
  29. 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<*>)
  30. 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<*>)
  31. 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<*>)
  32. 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)
  33. 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)
  34. 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)
  35. 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>()) }
  36. 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)
  37. 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>()
  38. 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>()
  39. 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>()
  40. 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>()
  41. 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>()
  42. 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>()) }
  43. 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!
  44. 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()) }
  45. 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>()
  46. 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>()
  47. 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>()
  48. 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>()
  49. 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
  50. 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>()
  51. 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<*>>() }
  52. 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<*>>() }
  53. 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<*>>() }
  54. 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<*>>() }
  55. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

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

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

    = listOf<BeanDefinition<*>>() inline fun <reified T> inject(): T { val clazz = T::class } }
  58. 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 } }
  59. 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 } }
  60. 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 } }
  61. 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 } }
  62. 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 } }
  63. 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") } }
  64. 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 } } }
  65. 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 } }
  66. class CoreContext { var instances = HashMap<KClass<*>, Any>() var definitions

    = listOf<BeanDefinition<*>>() fun <T : Module> build(module: T) {} inline fun <reified T> inject(): T {} }
  67. 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 {} }
  68. 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 {} }
  69. 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
  70. 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()) } } }
  71. 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()) } } }
  72. Reflection -> Kclass, KProperty, KFunction … -> Java => Extra

    lib And also … Extra Binding Lazy Inject
  73. 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
  74. Stream API (Kotlin on Java 8+) - toStream() Collection to

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

    {…} .filter {…} .toList() sequence.map {…} .filter {…} .toList()
  76. 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)
  77. @Test fun test() = runBlocking { val jobs = List(100_000)

    { launch(CommonPool) { delay(1000L) print(".") } } jobs.forEach { it.join() } }
  78. @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() } }
  79. @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() } }
  80. @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() } }
  81. @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() } }
  82. @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") }
  83. @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(".") }
  84. 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 {…}
  85. @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
 }
  86. @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
 }
  87. @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
 }
  88. @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
 }
  89. @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
 }
  90. @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
 }
  91. @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
 }
  92. @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
 }
  93. @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
 }
  94. @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
 }
  95. @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)
 }
 }
  96. @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)
 }
 }
  97. @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)
 }
 }
  98. @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)
 }
 }
  99. @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)
 }
 }
  100. @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)
 }
 }
  101. @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)
 }
 }
  102. @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)
 }
 }
  103. @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)
 }
 }
  104. @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)
 }
 }
  105. Coroutines - builder (runBlocking, async, launch …) - primitives (delay,

    measureTime, job…) - communication (deferred, channel, selector, actor …) => Reactive world (RxJava …) => UI (JavaFX, Android …) => Testing (?)
  106. 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