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

GeekOut_2019_-_Building_DSLs_in_Kotlin.pdf

 GeekOut_2019_-_Building_DSLs_in_Kotlin.pdf

As Kotlin programming language is getting more popular, the creators of the libraries are starting to provide Kotlin API for their frameworks. The number of libraries that provide a nice little DSL grows constantly. The session introduces you to some of those libraries and explains, how Kotlin makes creating the DSL so simple. Lambdas, extension methods, lambdas with the receiver, and other syntactic sugar make it easy to implement DSL in Kotlin. In a live coding demo, we will create a simple DSL for the existing Java classes that could be used from a Kotlin code.

1bc80e2eee2adeaa8bb577798d92e9d0?s=128

Anton Arhipov

September 27, 2019
Tweet

Transcript

  1. Building DSLs in Kotlin @antonarhipov

  2. Domain-specific, i.e.

  3. tailored for the specific task Domain-specific, i.e.

  4. External

  5. External VS Internal

  6. External VS Internal

  7. External VS Internal

  8. External VS Internal

  9. External VS Internal

  10. Internal

  11. createHTML().html { head { +"Hello!" } body { ul {

    p { +"This is my awesome text!" } } } } kotlinx.html https://github.com/Kotlin/kotlinx.html
  12. Anko https://github.com/Kotlin/anko verticalLayout { padding = dip(30) val name =

    editText { hint = "Name" textSize = 24f } val pwd = editText { hint = "Password" textSize = 24f } button("Login") { textSize = 26f onClick { toast("Hello, ${name.text}!") } }
  13. Gradle Kotlin DSL https://github.com/gradle/kotlin-dsl plugins { java kotlin("jvm") version "1.2.70"

    } group = "org.arhan" version = "1.0-SNAPSHOT" repositories { maven { setUrl("http://dl.bintray.com/kotlin/kotlin-eap") } jcenter() } dependencies { val kotlinx_html_version = "0.6.11" compile(kotlin("stdlib-jdk8")) compile("org.jetbrains.kotlinx:kotlinx-html-jvm:${kotlinx_html_version}") testCompile("junit", "junit", "4.12")
  14. Exposed https://github.com/JetBrains/Exposed object Cities : Table() { val id =

    integer("id").autoIncrement().primaryKey() // Column<Int> val name = varchar("name", 50) // Column<String> } transaction { create (Cities) val tallinnId = Cities.insert { it[name] = "Tallinn" } get Cities.id for (city in Cities.selectAll()) { println("${city[Cities.id]}: ${city[Cities.name]}") } }
  15. https://github.com/nfrankel/kaadin Kaadin theme = "valo" verticalLayout(margin = true, spacing =

    true) { tabSheet { tab("Interactions") { accordion { tab("Button", HAND_O_RIGHT) { horizontalLayout(true, true) { button() button("Label") button("Label", HAND_O_RIGHT) button("Click me", onClick = { show("Clicked") }) button("Click me", HAND_O_RIGHT, { show("Clicked") }) } }
  16. k8s-kotlin-dsl https://github.com/fkorotkov/k8s-kotlin-dsl val client = DefaultKubernetesClient().inNamespace("default") client.extensions().ingresses().createOrReplace( newIngress { metadata

    { name = "example-ingress" } spec { backend { serviceName = "example-service" servicePort = IntOrString(8080) } } } )
  17. Kotlin DSL in TeamCity project { vcsRoot(ApplicationVcs) buildType { id("Application")

    name = "Application" vcs { root(ApplicationVcs) } artifactRules = "target/*jar" steps { maven { goals = "clean package" } } triggers { vcs {} } dependencies { snapshot(Library) {}
  18. fun main(args: Array<String>) { embeddedServer(Jetty, commandLineEnvironment(args)).start(wait = true) } fun

    Application.main() { install(DefaultHeaders) install(CallLogging) routing { get("/") { call.respondHtml { head { title { +"Ktor App" } } body { p { +"Hello from Ktor!" } } Ktor
  19. fun main(args: Array<String>) { embeddedServer(Jetty, commandLineEnvironment(args)).start(wait = true) } fun

    Application.main() { install(DefaultHeaders) install(CallLogging) routing { get("/") { call.respondHtml { head { title { +"Ktor App" } } body { p { +"Hello from Ktor!" } } Ktor
  20. fun main(args: Array<String>) { embeddedServer(Jetty, commandLineEnvironment(args)).start(wait = true) } fun

    Application.main() { install(DefaultHeaders) install(CallLogging) routing { get("/") { call.respondHtml { head { title { +"Ktor App" } } body { p { +"Hello from Ktor!" } } Ktor
  21. They all look similar!

  22. foo { bar { baz = "Hello!" qux = quux

    { corge = "Blah" } } }
  23. foo { bar { baz = "Hello!" qux = quux

    { corge = "Blah" } } }
  24. foo { bar { baz = "Hello!" qux = quux

    { corge = "Blah" } } }
  25. foo { bar { baz = "Hello!" qux = quux

    { corge = "Blah" } } }
  26. foo { bar(grault = 1) { baz = "Hello!" qux

    = quux { corge = "Blah" } } }
  27. foo { bar(grault = 1) { baz = "Hello!" qux

    = quux { corge = Blah() } } }
  28. foo { bar(grault = 1) { baz = "Hello!" qux

    = quux { corge = Blah() } } }
  29. foo { bar(grault = 1) { baz = "Hello!" qux

    = quux { corge = Blah() } } }
  30. Let’s write some code! https://github.com/antonarhipov/kotlin-dsl-examples

  31. Let’s write some code! https://github.com/antonarhipov/kotlin-dsl-examples

  32. Type-safe builders https://kotlinlang.org/docs/reference/type-safe-builders.html

  33. final ClientBuilder builder = new ClientBuilder(); builder.setFirstName("Anton"); builder.setLastName("Arhipov"); final TwitterBuilder

    twitterBuilder = new TwitterBuilder(); twitterBuilder.setHandle("@antonarhipov"); builder.setTwitter(twitterBuilder.build()); final CompanyBuilder companyBuilder = new CompanyBuilder(); companyBuilder.setName("JetBrains"); companyBuilder.setCity("Tallinn"); builder.setCompany(companyBuilder.build()); final Client client = builder.build(); System.out.println("Created client is: " + client); val client = createClient { firstName = "Anton" lastName = "Arhipov" twitter { handle = "@antonarhipov" } company { name = "JetBrains" city = "Tallinn" } } println("Created client is: " + client.consoleString)
  34. //extension property val Client.consoleString: String get() = "${twitter.handle} ${company.name}" //extension

    method fun Client.toConsoleString(): String { return "${twitter.handle} ${company.name}" }
  35. fun createClient(c: ClientBuilder.() -> Unit): Client { val builder =

    ClientBuilder() c(builder) return builder.build() } fun ClientBuilder.company(t: CompanyBuilder.() -> Unit) { company = CompanyBuilder().apply(t).build() } fun ClientBuilder.twitter(t: CompanyBuilder.() -> Unit) { twitter = TwitterBuilder().apply(t).build() } Lambda with receiver Extension methods
  36. twitter { handle = "@antonarhipov" company { name = "JetBrains"

    city = "Tallinn" } } @DslMarker annotation class ClientDsl @ClientDsl class CompanyBuilderDsl : CompanyBuilder() @ClientDsl class TwitterBuilderDsl : TwitterBuilder() twitter { handle = "@antonarhipov" company { name = "JetBrains" city = "Tallinn" } } Scope control
  37. Infix notation https://kotlinlang.org/docs/reference/functions.html

  38. dateTime = LocalDateTime.of(2018, Month.DECEMBER, 11, 0, 0) dateTime = 11

    December 2018 at (14 hh 0) infix fun Int.December(n: Int) : LocalDate { return LocalDate.of(n, Month.DECEMBER, this) } infix fun LocalDate.at(n: Pair<Int, Int>): LocalDateTime { return this.atTime(n.first, n.second) } infix fun Int.hh(n: Int): Pair<Int, Int> { return Pair(this, n) }
  39. T.() -> Unit

  40. me { name = "Anton Arhipov" twitter = "@antonarhipov" }

    books { courses { Kotlin { Try = "try.kotlinlang.org" Slack = "slack.kotl.in" }