Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DSL Design (Kotlin Budapest User Group meetup - September)

Márton Braun
September 28, 2017

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.

Márton Braun

September 28, 2017
Tweet

More Decks by Márton Braun

Other Decks in Programming

Transcript

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

    features • Common examples • My own experience • Design demo • How to implement DSLs
  2. Domain Specific Languages • Programming languages • Specialized to solve

    problems in a particular domain  HTML  Gradle build language • Often in a declarative style
  3. DSL ingredients in Kotlin • Extension functions • Extension properties

    • Lambdas with receivers • Operator overloading  Unary operators  invoke operator • Infix functions • @DslMarker annotation
  4. html { body { div { a("http://kotlinlang.org") { target =

    ATarget.blank +"Main site" } } } } kotlinx.html https://github.com/Kotlin/kotlinx.html
  5. 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. 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. 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. 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. 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. 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. 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