binout / kotlin-conf-2018

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

About

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

License:Apache License 2.0