Slide 1

Slide 1 text

DSL design Márton Szabolcs Braun zsmb13 [email protected] zsmb13

Slide 2

Slide 2 text

About this talk • What’s a DSL • Relevant Kotlin features • Common examples • My own experience • Design demo • How to implement DSLs

Slide 3

Slide 3 text

Domain Specific Languages • Programming languages • Specialized to solve problems in a particular domain  HTML  Gradle build language • Often in a declarative style

Slide 4

Slide 4 text

DSL ingredients in Kotlin • Extension functions • Extension properties • Lambdas with receivers • Operator overloading  Unary operators  invoke operator • Infix functions • @DslMarker annotation

Slide 5

Slide 5 text

Common examples

Slide 6

Slide 6 text

Anko https://github.com/Kotlin/anko verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } }

Slide 7

Slide 7 text

html { body { div { a("http://kotlinlang.org") { target = ATarget.blank +"Main site" } } } } kotlinx.html https://github.com/Kotlin/kotlinx.html

Slide 8

Slide 8 text

Gradle Script Kotlin android { buildToolsVersion("25.0.0") compileSdkVersion(23) defaultConfig { minSdkVersion(15) targetSdkVersion(23) applicationId = "com.example.myapp" versionCode = 1 versionName = "1.0" } } https://github.com/gradle/kotlin-dsl android { compileSdkVersion 23 buildToolsVersion "25.0.0" defaultConfig { minSdkVersion 15 targetSdkVersion 23 applicationId "com.example.myapp" versionCode 1 versionName "1.0" } } Kotlin Groovy

Slide 9

Slide 9 text

object Cities : Table() { val id = integer("id").autoIncrement().primaryKey() val name = varchar("name", 50) } transaction { val munichId = Cities.insert { it[name] = "Munich" } get Cities.id Cities.insert { it[name] = "Prague" } } Exposed https://github.com/JetBrains/Exposed

Slide 10

Slide 10 text

My experience

Slide 11

Slide 11 text

MaterialDrawerKt https://github.com/zsmb13/MaterialDrawerKt drawer { closeOnClick = false sectionHeader("Standard badges") { divider = false } primaryItem("Primary") { badge("111") iicon = FontAwesome.Icon.faw_heart } sectionHeader("Customized badges") primaryItem("Custom 1") { badge("111") { color = 0xFF0099FF colorPressed = 0xFFCC99FF cornersDp = 0 paddingHorizontalDp = 25 } iicon = FontAwesome.Icon.faw_heart selectable = false } }

Slide 12

Slide 12 text

Tests for Twitter responses Status MediaEntity Array< > URLEntity Array< > HashtagEntity Array< > UserMentionEntity Array< > val status: Status = mock() whenever(status.text) doReturn mockText whenever(status.mediaEntities) doReturn mockMediaEntities whenever(status.urlEntities) doReturn mockUrls whenever(status.userMentionEntities) doReturn mockUserMentionEntities whenever(status.hashtagEntities) doReturn mockHashtagEntities

Slide 13

Slide 13 text

Tests for Twitter responses val status: Status = mock() whenever(status.text) doReturn mockText whenever(status.mediaEntities) doReturn mockMediaEntities whenever(status.urlEntities) doReturn mockUrls whenever(status.userMentionEntities) doReturn mockUserMentionEntities whenever(status.hashtagEntities) doReturn mockHashtagEntities Status MediaEntity Array< > URLEntity Array< > HashtagEntity Array< > UserMentionEntity Array< > val entity: UserMentionEntity = mock() whenever(entity.start) doReturn mockStart whenever(entity.end) doReturn mockEnd whenever(entity.id) doReturn mockId whenever(entity.screenName) doReturn mockScreenName val entity: MediaEntity = mock() whenever(entity.start) doReturn mockStart whenever(entity.end) doReturn mockEnd whenever(entity.url) doReturn mockUrl val entity: URLEntity = mock() whenever(entity.start) doReturn mockStart whenever(entity.end) doReturn mockEnd whenever(entity.url) doReturn mockUrl whenever(entity.displayURL) doReturn mockDisplayUrl whenever(entity.expandedURL) doReturn mockExpandedUrl val entity: HashtagEntity = mock() whenever(entity.start) doReturn mockStart whenever(entity.end) doReturn mockEnd whenever(entity.text) doReturn mockText val h1 = createHashtag(1, 4, "cool") val h2 = createHashtag(6, 12, "awesome") val hashtags = arrayOf(h1, h2) val status = createStatus(hashtags, mentions, ...)

Slide 14

Slide 14 text

tests { testCase("emoji and text") { input("hello world") expected("hello world") } testCase("long, complicated status") { input("hello t.co/a t.co/b t.co/m1 t.co/c t.co/m2 ") { url(9 to 15, "t.co/a", "a.com", "a.com") url(19 to 25, "t.co/b", "b.com", "b.com") media(29 to 36, "t.co/m1") url(40 to 46, "t.co/c", "c.c.c.com", "c.c.c.com") media(50 to 57, "t.co/m2") } expected("hello a.com b.com c.c.c.com ") { url(13 to 18, "t.co/a", "a.com", "a.com") url(26 to 31, "t.co/b", "b.com", "b.com") url(47 to 56, "t.co/c", "c.c.c.com", "c.c.c.com") } } } Tests for Twitter responses

Slide 15

Slide 15 text

Demo

Slide 16

Slide 16 text

Resources • Village DSL  https://github.com/zsmb13/VillageDSL • Type safe builders (official documentation)  https://kotlinlang.org/docs/reference/type-safe-builders.html • Designing a DSL in Kotlin (Nicolas Fränkel)  https://youtu.be/fD3OqrAeaWo • Creating DSL's in idiomatic Kotlin (Hadi Hariri)  https://youtu.be/GjGQpSFieXA

Slide 17

Slide 17 text

Photo: Alexey Sergeev DSL design zsmb13 [email protected] zsmb13