DSL Design (Kotlin Budapest User Group meetup - September)

DSL Design (Kotlin Budapest User Group meetup - September)

A talk presenting the basics of what a DSL is, what use cases there are for them, and looking at some of the design choices we can make while creating one.

4047c64e3a1e2f81addd4ba675ddc451?s=128

Marton Braun

September 28, 2017
Tweet

Transcript

  1. 2.

    About this talk • What’s a DSL • Relevant Kotlin

    features • Common examples • My own experience • Design demo • How to implement DSLs
  2. 3.

    Domain Specific Languages • Programming languages • Specialized to solve

    problems in a particular domain  HTML  Gradle build language • Often in a declarative style
  3. 4.

    DSL ingredients in Kotlin • Extension functions • Extension properties

    • Lambdas with receivers • Operator overloading  Unary operators  invoke operator • Infix functions • @DslMarker annotation
  4. 7.

    html { body { div { a("http://kotlinlang.org") { target =

    ATarget.blank +"Main site" } } } } kotlinx.html https://github.com/Kotlin/kotlinx.html
  5. 8.

    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
  6. 9.

    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
  7. 11.

    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 } }
  8. 12.

    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
  9. 13.

    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, ...)
  10. 14.

    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
  11. 15.
  12. 16.

    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