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

Beyond Kotlin - Advanced features for API Makers

Beyond Kotlin - Advanced features for API Makers

Kotlin talk for experimented developers

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