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

JavaZone 2022 - Building Kotlin DSL

Anton Arhipov
September 08, 2022

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

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

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

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

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

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

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

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

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

    method fun Client.toConsoleString(): String { return "${twitter.handle} ${company.name}" }
  11. 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
  12. 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) }
  13. 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
  14. Kotlin { YouTube = "youtube.com/kotlin" Slack = "slack.kotl.in" } me

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