$30 off During Our Annual Pro Sale. View Details »

JavaZone 2022 - Building Kotlin DSL

JavaZone 2022 - Building Kotlin DSL

Kotlin provides many features that make your code look like a domain-specific language. These features make Kotlin an excellent choice in cases where we want to express program logic declaratively. The approach is already adopted in many tools and frameworks: you can describe build logic in Gradle with Kotlin, Spring WebFlux uses syntax features for its APIs, etc. This session will teach you how to create type-safe DSLs in Kotlin. We will look at a few practical examples and produce a simple DSL.

#kotlin

Anton Arhipov

September 08, 2022
Tweet

More Decks by Anton Arhipov

Other Decks in Programming

Transcript

  1. Kotlin DSL Building @antonarhipov

  2. @antonarhipov

  3. Why Kotlin?

  4. Null-safety Coroutines Multiplatform Syntax Why Kotlin?

  5. Null-safety Coroutines Multiplatform Syntax Why Kotlin?

  6. Null-safety Coroutines Multiplatform Syntax Why Kotlin?

  7. Null-safety Coroutines Multiplatform Syntax Why Kotlin?

  8. Null-safety Coroutines Multiplatform Syntax Why Kotlin? DSL

  9. “Domain-speci fi c”, i.e.

  10. tailored for a speci fi c task “Domain-speci fi c”,

    i.e.
  11. External

  12. External VS Internal

  13. External VS Internal

  14. External VS Internal

  15. External VS Internal

  16. External VS Internal

  17. Internal

  18. None
  19. None
  20. +

  21. None
  22. None
  23. None
  24. None
  25. They all look similar!

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

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

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

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

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

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

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

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

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

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

  36. 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)
  37. //extension property val Client.consoleString: String get() = "${twitter.handle} ${company.name}" //extension

    method fun Client.toConsoleString(): String { return "${twitter.handle} ${company.name}" }
  38. 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
  39. In fi x notation https://kotlinlang.org/docs/reference/functions.html

  40. 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) }
  41. Context Receivers KEEP-259 Experimental

  42. context(DateContext) @DateDsl infix fun Int.March(n: Int): LD = LD.of(n, Month.MARCH,

    this) class DateContext val client = createClient { // .. . dob = 24 March 2000 } 24 March 2000 fun client(block: context(DateContext) ClientBuilder.() - > Unit): Client { val builder = ClientBuilder() with(DateContext()) { block(builder) } return builder.build() } Context object Context requirement Context requirement Context scope The function is available in the context scope The function is not available, missing DateContext
  43. T.() -> Unit

  44. Kotlin { YouTube = "youtube.com/kotlin" Slack = "slack.kotl.in" } me

    { name = "Anton Arhipov" twitter = "@antonarhipov" slides = speakerdeck.com/antonarhipov } books { courses {