/kotlin-conf-2018

Notes about Kotlin Conf 2018 (https://kotlinconf.com/)

Apache License 2.0Apache-2.0

Kotlin Conf

DSL in Kotlin

When you really wish your language could do the thing

Ktor

what is ktor shadow

Advices

Start from the result that you want to enable
…​ then write the code thant enables it !

@DslMarker : prevent scoping mishaps !

  • Dont' pollute the global namespace

  • Unary + only if well-scoped

  • Keep lambda files next to your builder classes

  • Don’t extend system types (String, Int, …​)

42 { //bad
.....
}

100.dollarsToCent() // Good

Kotlin and Spring Boot, a match made in heaven

@nicolas_frankel

🤯 The issue : MAGIC !

🧐 The solution ? : functional configuration

Switch to reactive :

  • JPA → Mongo Reactive

  • WebMVC → WebFlux

Swith to static configuration (no magic) :

  • RestController → static route definitions → Router definition DSL

  • @Beans → Bean Definition DSL

  • context.initializer.classes property → programmtically register BeansInitializer

SpringFu

Warning
Experimental 😜
kofu (no more annotations !)
val beans = beans {
    bean<PersonHandler>()
    bean<PersonRepository>()
}

val app = application {
    import(beans)
    listener<ApplicationReadyEvent> {
        ref<PersonRepository>().insert(
                arrayListOf(Person(1, "John", "Doe", LocalDate.of(1970, 1, 1)),
                        Person(2, "Jane", "Doe", LocalDate.of(1970, 1, 1)),
                        Person(3, "Brian", "Goetz"))
        ).blockLast(Duration.ofSeconds(2))
    }
    server {
        import(::routes)
        codecs {
            jackson()
        }
    }
    mongodb {
        embedded()
    }
}

fun routes(handler: PersonHandler) = router {
    "/person".nest {
        GET("/{id}", handler::readOne)
        GET("/", handler::readAll)
    }
}

class PersonHandler(private val personRepository: PersonRepository) {
    fun readAll(request: ServerRequest) = ServerResponse.ok().body(personRepository.findAll())
    fun readOne(request: ServerRequest) = ServerResponse.ok().body(personRepository.findById(request.pathVariable("id").toLong()))
}

fun main(args: Array<String>) {
    app.run(args)
}

@Document
class Person(@Id val id: Long, val firstName: String, val lastName: String, val birthdate: LocalDate? = null)

class PersonRepository(private val mongo: ReactiveMongoOperations) {
    fun findAll() = mongo.findAll<Person>()
    fun findById(id: Long) = mongo.findById<Person>(id)
    fun insert(persons: List<Person>) = mongo.insert(persons, Person::class)
}

GraphQL powered by Kotlin

GraphQL server

GraphQL Type
type UFOSighting {
    id : Int!
    city: String
}
KGraphQL
type <UFOSighting>

data class UFOSighting {
    id : Int = -1
    city: String?
}

GraphQL client

Generate Java Client from schema.json

  1. Build your request

  2. Enqueue the resquest

  3. Handle the response

Tip
  • Intellij GraphQL plugin

  • Retrofit GraphQL

graphql shorthand notation cheat sheet

Architecting a Kotlin JVM and JS multiplatform project

Ideal for businnes logic code sharing
Kotlin Multiplatform != React Native
Kotlin Multiplatform > C / C++

Common

→ kotlinc (JVM, Android)

→ Kotlin/Native (Executable, Dynamic lib, iOS)

→ kotlin2js (Javascript)

Gradle plugins

  • apply plugin: 'kotlin-platform-common'

  • apply plugin: 'kotlin-platform-jvm'

  • apply plugin: 'org.jetbrains.kotlin.frontend

  • …​

Concept

Common
expect class Order {
    val id: Int
    val userId: Int
}
JVM
actual data class Order {
    val id: Int
    val userId: Int
}

expect is not interface !

  • simplier implementation

  • can have a constructor

  • all implementations are known at compile time

  • more flexibility

  • top level and extension functions are supported

Warning
  • Cannot reference any platform specific code

  • Can only have kotlin code

  • Can depend only platform common lib

Exploring the Kotlin type hierarchy from top to bottom

👍👍👍👍👍👍

Tip
you need to explicity opt in at the call site to use experimental features : kotlin { experimental { contracts 'enable'}
Tip

you can mark your experimental API with :

@Experimental
annotation class ShinyNewAPI

@ShinyNewAPI
class Foo

Contracts

We know something about run, which the compiler doesn’t

Contracts allow to share extra information about code semantics with the compiler

  • Making smart casts even smarter

fun String?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }
    return this == "" || this == null
}

val s: String? = ""
if (!s.isNullOrEmpty) {
    s.first() // ✅
}

New type inference

  • Better and more powerful type inference

  • New Features are supported

kotlin { experimental { newInference 'enable'}

Tip
Libraries should specify return types for public API : turn on the IDE inspection ("Public API delcaration has implicit return type")
  • Function Interface conversions for Kotlin functions

  • better inference for builders

  • better inference for call chains

  • better inference for intersection types

Representing State: the Kotlin Edition

👻 Boolean Blindness ⇒ work with more expressive types !

  • use sealed classes (everywhere !)

  • use interfaces for boolean representation

💥 Strings are danger (same for Int) ⇒ infinite input 😱

Limit state !
class IllogicalPerson {
    var heart: Heart?
    var head: Head?
    var arms: List<Arm>
    var legs: List<Leg>
}

class LogicalPerson {
    var heart: Heart
    val head: Head
    val arms: Pair<Arm?,Arm?>
    val legs: Pair<Leg?,Leg?>
}

😻😻😻😻

Exploring Coroutines in Kotlin

Parallel Stream
Structure of [functional] sequential code is the same as parallel code
Coroutine
Structure of [imperative] synchronous code is the same as asynchronous code

🤩😎🤩😎🤩😎🤩😎🤩😎🤩😎

Functional Programming in Kotlin with Λrrow

arrow brand sidebar

Immutable model

Pure

  • Don’t throw exceptions → use Either and Try but it’s synchronous

  • Arrow provides Monad Transformers : EitherT

Async non-blocking

KEEP-87

MR to add Type Class in Kotlin : Kotlin/KEEP#87

Type class declaration
interface Repository<A> {
    fun A.save(): A
    fun cache(): List<A>
}

Building Server Backends with Ktor

Composable, DSL based web services in Kotlin
Warning
1.0 waiting for Kotlin 1.3 (corountine no more experimental)
ktor
fun Application.verify() {
    install(StatusPages) {...}
    install(ContentNegociation) {...}
    routing {
        post("/verify") {
            call.respond(Response(status="OK")
        }
    }
}

data class Response(val status: String)

Best Practices for Unit Testing in Kotlin

Junit 5

  • Reuse the Test Class Instance : @TestInstance(TestInstance.Lifecycle.PER_CLASS)
    → you can use private val field or init {…​} block

TIP : you can set the lifecycle by defaut in junit-platform.properties file (no more need annotation)

  • Use backticks for test name

  • Use @Nested to group tests (by tested method of class for instance)

Kotlin Test Libraries

test

TIP : write test specific extension for AssertJ

Mocking

Warning
☠️ Classes Are Final by Default

⇒ use MockK (https://mockk.io/)

  • Don’t recreate Mocks ! (it’s expensive : 2,1s → 0,4s )

@BeforeEach
fun init() {
    clearMocks(repo, client)
}

Spring Integration

  • Use maven plugin allopen to deal with final classes (✅)

  • Use constructor injection (👍)

Data class

  • Use for Assertions !

Tip
assertThat(…​).isEqualToIgnoringGivendFields(…​, "id")
  • Helper function for Object Creation (use default values for data class args)

  • Data classes for Parameterized Test with @MethodSource

Summary

test summary

Creating Internal DSLs in Kotlin