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.

2aec47eb9a940c619f05972f0db5aa00?s=128

Kirill Rozov

June 25, 2019
Tweet

Transcript

  1. Andropower of Kotlin DSL

  2. Kirill Rozov krl.rozov@gmail.com Lead Android Developer@

  3. android_broadcast News for Android Devs

  4. Agenda • What is DSL? • Kotlin DSL • How

    to build own DSL • Where to apply Kotlin DSL
  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
  6. Examples of DSL Languages • SQL • HTML • CSS

    • Regular expressions
  7. DSL positives

  8. DSL positives +Stricter API for specific domain

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

    tasks
  10. DSL negatives

  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
  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
  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
  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
  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
  16. <!DOCTYPE html> <html> <head> <title>This is a title</title> </head> <body>

    <p>Hello</p> <p>world!</p> </body> </html> DSL Sample .html
  17. DSL Sample .java

  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
  19. val html = html { head { title("This is a

    title") } body { p("Hello") p("world!") } } DSL Sample .kt
  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
  21. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content"

    /> <Button android:id="@+id/say_hello" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> Android Layout XML
  22. verticalLayout { val name = editText() button("Say Hello") { onClick

    { toast("Hello, ${name.text}!") } } } Anko UI
  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
  24. Kotlin DSL building blocks

  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
  26. Lambda with receiver fun buildString(body: (StringBuilder) -> Unit): String {

    val builder = StringBuilder() body(builder) return builder.toString() }
  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") }
  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 ->
  29. Lambda with receiver fun buildString(body: (StringBuilder) -> Unit): String {

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

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

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

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

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

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

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

    val builder = StringBuilder() builder.body() return builder.toString() }
  37. Lambda with receiver buildString { append("Hello") append(' ') append("world") }

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

    this: StringBuilder
  39. with from std library inline fun <T, R> with(receiver: T,

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

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

    } new StringBuilder() .append("Hello") .append(' ') .append("world");
  42. StrictModeCompat github.com/kirich1409/StrictModeCompat

  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
  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
  45. fun initStrictMode( config: StrictModeConfig.() -> Unit ) { … }

    Android StrictMode Sample
  46. fun initStrictMode( config: StrictModeConfig.() -> Unit ) { … }

    Android StrictMode Sample
  47. fun initStrictMode( config: StrictModeConfig.() -> Unit ) { … }

    Android StrictMode Sample
  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
  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
  50. class PenaltyConfig { var death = false var deathOnNetwork =

    false var dialog = false var dropBox = false var flashScreen = false var log = false } Android StrictMode Sample
  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
  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
  53. initStrictMode { threadPolicy { resourceMismatches = true unbufferedIo = true

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

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

    penalty { log = true threadPolicy { … } } } } Android StrictMode Sample this: StrictModeConfig this: ThreadPolicyConfig this: PenaltyConfig
  56. @DslMarker annotation class StrictModeDsl Android StrictMode Sample

  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
  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
  59. initStrictMode { threadPolicy { resourceMismatches = true unbufferedIo = true

    penalty { log = true threadPolicy { … } } } } Android StrictMode Sample
  60. Where to apply Kotlin DSL ? • As replacement of

    the Builder pattern • As mediator between different DSL and Kotlin code
 SQL, HTML, etc
  61. Ideas to do

  62. Ideas to do • Android System Notification

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

    String
 Android-KTX already have basic implementation's
  64. Ideas to do • Android System Notification • Android Spannable

    String
 Android-KTX already have basic implementation's • Alert Dialogs
  65. Thank YOU!!!