$30 off During Our Annual Pro Sale. View Details »

Andropower of Kotlin DSL

Andropower of Kotlin DSL

DSL is a good way to write code declaratively. Kotlin simplifies many things for us and gave the possibility to write our own DSL for APIs. In the talk, you can find out how to write Kotlin DSL and how it can be applied to improve Android APIs.

Kirill Rozov

June 25, 2019
Tweet

More Decks by Kirill Rozov

Other Decks in Programming

Transcript

  1. Andropower of Kotlin DSL

    View Slide

  2. Kirill Rozov
    [email protected]
    Lead Android Developer@

    View Slide

  3. android_broadcast
    News for Android Devs

    View Slide

  4. Agenda
    • What is DSL?

    • Kotlin DSL

    • How to build own DSL

    • Where to apply Kotlin DSL

    View Slide

  5. A domain-specific language (DSL) is a computer language
    specialised to a particular application domain. DSL is opposite for
    general-purpose language (GPL), which is broadly applicable across
    domains
    wikipedia.org/wiki/Domain-specific_language

    View Slide

  6. Examples of DSL Languages
    • SQL

    • HTML

    • CSS

    • Regular expressions

    View Slide

  7. DSL positives

    View Slide

  8. DSL positives
    +Stricter API for specific domain

    View Slide

  9. DSL positives
    +Stricter API for specific domain
    +Better solve special tasks

    View Slide

  10. DSL negatives

    View Slide

  11. DSL negatives
    — Non-trivial to validate the correct interaction of the DSL with the host
    language at compile time

    Harder to debug the DSL program and to provide IDE code completion

    View Slide

  12. DSL negatives
    — Non-trivial to validate the correct interaction of the DSL with the host
    language at compile time

    Harder to debug the DSL program and to provide IDE code completion
    — Can be difficult to combine with a host application in a GPL

    Need to either store program written in DSL in a separate file or embed it in a string literal

    View Slide

  13. DSL negatives
    — Non-trivial to validate the correct interaction of the DSL with the host
    language at compile time

    Harder to debug the DSL program and to provide IDE code completion
    — Can be difficult to combine with a host application in a GPL

    Need to either store program written in DSL in a separate file or embed it in a string literal
    — The separate syntax requires separate learning

    View Slide

  14. As opposed to external DSLs, which have their own independent
    syntax, internal DSLs are part of programs written in a general-
    purpose language, using exactly the same syntax

    View Slide

  15. As opposed to external DSLs, which have their own independent
    syntax, internal DSLs are part of programs written in a general-
    purpose language, using exactly the same syntax
    … and Kotlin has it

    View Slide




  16. This is a title


    Hello
    world!


    DSL Sample .html

    View Slide

  17. DSL Sample .java

    View Slide

  18. HtmlBuilder htmlBuilder = createHtml();
    htmlBuilder.head()
    .title("This is a title");
    htmlBuilder.body()
    .add(p("Hello”))
    .add(p("world!"));
    String html = htmlBuilder.build();
    DSL Sample .java

    View Slide

  19. val html = html {
    head {
    title("This is a title")
    }
    body {
    p("Hello")
    p("world!")
    }
    }
    DSL Sample .kt

    View Slide

  20. Libraries with Kotlin DSL
    • Kotlin Anko UI DSL

    github.com/Kotlin/anko

    • Gradle Kotlin DSL

    github.com/gradle/kotlin-dsl

    • kotlinx.html

    github.com/Kotlin/kotlinx.html

    • Spek

    spekframework.org

    • Kakao

    github.com/agoda-com/Kakao

    View Slide

  21. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    android:id="@+id/name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
    android:id="@+id/say_hello"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

    Android Layout XML

    View Slide

  22. verticalLayout {
    val name = editText()
    button("Say Hello") {
    onClick { toast("Hello, ${name.text}!") }
    }
    }
    Anko UI

    View Slide

  23. apply(plugin = "java-library")
    dependencies {
    "api"("junit:junit:4.12")
    "implementation"("junit:junit:4.12")
    "testImplementation"("junit:junit:4.12")
    }
    configurations {
    "implementation" {
    resolutionStrategy.failOnVersionConflict()
    }
    }
    Gradle Kotlin DSL

    View Slide

  24. Kotlin DSL building blocks

    View Slide

  25. Kotlin DSL building blocks
    • Extension function

    String.capitalize()

    • Operator overloading

    mutableList += “item”

    • Infix function call

    1 to “one”

    • Lambda outside of parentheses

    reader.use { it.readLine() }

    • Lambda with receiver

    StringBuilder.() -> Unit

    View Slide

  26. Lambda with receiver
    fun buildString(body: (StringBuilder) -> Unit): String {
    val builder = StringBuilder()
    body(builder)
    return builder.toString()
    }

    View Slide

  27. Lambda with receiver
    fun buildString(body: (StringBuilder) -> Unit): String {
    val builder = StringBuilder()
    body(builder)
    return builder.toString()
    }
    buildString {
    it.append("Hello")
    it.append(' ')
    it.append("world")
    }

    View Slide

  28. Lambda with receiver
    fun buildString(body: (StringBuilder) -> Unit): String {
    val builder = StringBuilder()
    body(builder)
    return builder.toString()
    }
    buildString {
    it.append("Hello")
    it.append(' ')
    it.append("world")
    }
    it: StringBuilder ->

    View Slide

  29. Lambda with receiver
    fun buildString(body: (StringBuilder) -> Unit): String {
    val builder = StringBuilder()
    body(builder)
    return builder.toString()
    }

    View Slide

  30. Lambda with receiver
    fun buildString(body: StringBuilder.() -> Unit): String {
    val builder = StringBuilder()
    body(builder)
    return builder.toString()
    }

    View Slide

  31. Lambda with receiver
    fun buildString(body: StringBuilder.() -> Unit): String {
    val builder = StringBuilder()
    builder.body()
    return builder.toString()
    }

    View Slide

  32. Lambda with receiver
    fun buildString(body: StringBuilder.() -> Unit): String {
    val builder = StringBuilder()
    builder.body()
    return builder.toString()
    }

    View Slide

  33. Lambda with receiver
    fun buildString(body: StringBuilder.() -> Unit): String {
    val builder = StringBuilder()
    builder.body()
    return builder.toString()
    }

    View Slide

  34. Lambda with receiver
    fun buildString(body: StringBuilder.() -> Unit): String {
    val builder = StringBuilder()
    builder.body()
    return builder.toString()
    }

    View Slide

  35. Lambda with receiver
    fun buildString(body: StringBuilder.() -> Unit): String {
    val builder = StringBuilder()
    builder.body()
    return builder.toString()
    }

    View Slide

  36. Lambda with receiver
    fun buildString(body: StringBuilder.() -> Unit): String {
    val builder = StringBuilder()
    builder.body()
    return builder.toString()
    }

    View Slide

  37. Lambda with receiver
    buildString {
    append("Hello")
    append(' ')
    append("world")
    }

    View Slide

  38. Lambda with receiver
    buildString {
    append("Hello")
    append(' ')
    append("world")
    }
    this: StringBuilder

    View Slide

  39. with from std library
    inline fun with(receiver: T, block: T.() -> R): R {
    return receiver.block()
    }
    with(StringBuilder()) {
    append("Hello")
    append(' ')
    append("world")
    }

    View Slide

  40. with from std library
    with(StringBuilder()) {
    append("Hello")
    append(' ')
    append("world")
    }

    View Slide

  41. with from std library
    with(StringBuilder()) {
    append("Hello")
    append(' ')
    append("world")
    }
    new StringBuilder()
    .append("Hello")
    .append(' ')
    .append("world");

    View Slide

  42. StrictModeCompat
    github.com/kirich1409/StrictModeCompat

    View Slide

  43. StrictMode.ThreadPolicy threadPolicy =
    new StrictModeCompat.ThreadPolicy.Builder()
    .detectResourceMismatches()
    .detectUnbufferedIo()
    .penaltyLog()
    .build();
    StrictMode.VmPolicy vmPolicy = new StrictModeCompat.VmPolicy.Builder()
    .detectFileUriExposure()
    .detectLeakedRegistrationObjects()
    .detectContentUriWithoutPermission()
    .penaltyLog()
    .penaltyFlashScreen()
    .build();
    StrictModeCompat.setPolicies(threadPolicy, vmPolicy);
    Android StrictMode Sample

    View Slide

  44. initStrictMode {
    threadPolicy {
    resourceMismatches = true
    unbufferedIo = true
    penalty {
    log = true
    }
    }
    vmPolicy {
    fileUriExposure = true
    leakedRegistrationObjects = true
    contentUriWithoutPermission = true
    penalty {
    log = true
    flashScreen = true
    }
    }
    }
    Android StrictMode Sample

    View Slide

  45. fun initStrictMode(
    config: StrictModeConfig.() -> Unit
    ) {

    }
    Android StrictMode Sample

    View Slide

  46. fun initStrictMode(
    config: StrictModeConfig.() -> Unit
    ) {

    }
    Android StrictMode Sample

    View Slide

  47. fun initStrictMode(
    config: StrictModeConfig.() -> Unit
    ) {

    }
    Android StrictMode Sample

    View Slide

  48. class StrictModeConfig {
    var threadPolicyConfig = ThreadPolicyConfig()
    private set
    var vmPolicyConfig = VmPolicyConfig()
    private set
    fun threadPolicy(config: ThreadPolicyConfig.() -> Unit) {
    threadPolicyConfig.apply(config)
    }
    fun vmPolicy(config: VmPolicyConfig.() -> Unit) {
    vmPolicyConfig.apply(config)
    }
    }
    Android StrictMode Sample

    View Slide

  49. class ThreadPolicyConfig {
    var customSlowCalls = false
    var diskReads = false
    var diskWrites = false
    var network = false
    var resourceMismatches = false
    var unbufferedIo = false
    var penaltyConfig = PenaltyConfig()
    private set
    fun penalty(config: PenaltyConfig.() -> Unit) {
    penaltyConfig.apply(config)
    }
    }
    Android StrictMode Sample

    View Slide

  50. class PenaltyConfig {
    var death = false
    var deathOnNetwork = false
    var dialog = false
    var dropBox = false
    var flashScreen = false
    var log = false
    }
    Android StrictMode Sample

    View Slide

  51. fun initStrictMode(
    config: StrictModeConfig.() -> Unit
    ) {
    StrictModeConfig().apply {
    config()
    val threadPolicy =
    buildThreadPolicy(threadPolicyConfig)
    StrictMode.setThreadPolicy(threadPolicy)
    val vmPolicy = buildVmPolicy(vmPolicyConfig)
    StrictMode.setVmPolicy(vmPolicy)
    }
    }
    Android StrictMode Sample

    View Slide

  52. fun buildThreadPolicy(
    config: ThreadPolicyConfig
    ): ThreadPolicy {
    val builder = StrictModeCompat.ThreadPolicy.Builder()
    if (config.customSlowCalls) builder.detectCustomSlowCalls()
    if (config.diskReads) builder.detectDiskReads()

    config.penaltyConfig.let {
    if (it.death) builder.penaltyDeath()
    if (it.deathOnNetwork) builder.penaltyDeathOnNetwork()

    }
    return threadPolicyBuilder.build()
    }
    Android StrictMode Sample

    View Slide

  53. initStrictMode {
    threadPolicy {
    resourceMismatches = true
    unbufferedIo = true
    penalty {
    log = true
    }
    }
    }
    Android StrictMode Sample
    this: StrictModeConfig
    this: ThreadPolicyConfig
    this: PenaltyConfig

    View Slide

  54. initStrictMode {
    threadPolicy {
    resourceMismatches = true
    unbufferedIo = true
    penalty {
    log = true
    threadPolicy {

    }
    }
    }
    }
    Android StrictMode Sample
    this: StrictModeConfig
    this: ThreadPolicyConfig
    this: PenaltyConfig

    View Slide

  55. initStrictMode {
    threadPolicy {
    resourceMismatches = true
    unbufferedIo = true
    penalty {
    log = true
    threadPolicy {

    }
    }
    }
    }
    Android StrictMode Sample
    this: StrictModeConfig
    this: ThreadPolicyConfig
    this: PenaltyConfig

    View Slide

  56. @DslMarker
    annotation class StrictModeDsl
    Android StrictMode Sample

    View Slide

  57. class StrictModeConfig() {
    var threadPolicyConfig = ThreadPolicyConfig()
    private set
    var vmPolicyConfig = VmPolicyConfig()
    private set
    fun threadPolicy(
    config: ThreadPolicyConfig.() -> Unit
    ) {
    threadPolicyConfig.apply(config)
    }
    fun vmPolicy(config: VmPolicyConfig.() -> Unit) {
    vmPolicyConfig.apply(config)
    }
    }
    Android StrictMode Sample

    View Slide

  58. @StrictModeDsl
    class StrictModeConfig() {
    var threadPolicyConfig = ThreadPolicyConfig()
    private set
    var vmPolicyConfig = VmPolicyConfig()
    private set
    fun threadPolicy(
    config: @StrictModeDsl ThreadPolicyConfig.() -> Unit
    ) {
    threadPolicyConfig.apply(config)
    }
    fun vmPolicy(config: @StrictModeDsl VmPolicyConfig.() -> Unit) {
    vmPolicyConfig.apply(config)
    }
    }
    Android StrictMode Sample

    View Slide

  59. initStrictMode {
    threadPolicy {
    resourceMismatches = true
    unbufferedIo = true
    penalty {
    log = true
    threadPolicy {

    }
    }
    }
    }
    Android StrictMode Sample

    View Slide

  60. Where to apply Kotlin DSL ?
    • As replacement of the Builder pattern

    • As mediator between different DSL and Kotlin code

    SQL, HTML, etc

    View Slide

  61. Ideas to do

    View Slide

  62. Ideas to do
    • Android System Notification

    View Slide

  63. Ideas to do
    • Android System Notification
    • Android Spannable String

    Android-KTX already have basic implementation's

    View Slide

  64. Ideas to do
    • Android System Notification
    • Android Spannable String

    Android-KTX already have basic implementation's
    • Alert Dialogs

    View Slide

  65. Thank YOU!!!

    View Slide