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

Kotlin DSLs and How to Build Them

Kotlin DSLs and How to Build Them

Domain Specific Language (DSL) using Kotlin are increasingly becoming a prominent part of the impact of the language. They can be used to write type-safe, structured code while keeping it highly readable.

In this talk, I go through what a DSL is, why Kotlin makes it relevant and the important Koltin features that allow us to write great DSLs. I also go through the process of creating a custom DSL and things to keep in mind while making one. By the end of this talk, the attendees will have a good understanding of DSL in Kotlin and how to create one for their use case.

Subhrajyoti Sen

August 02, 2019
Tweet

More Decks by Subhrajyoti Sen

Other Decks in Programming

Transcript

  1. Domain Specific Language A DSL is a computer language specialized

    to a particular application domain. This is contrast to a general-purpose language (GPL), which is broadly applicable across domains. -Wikipedia
  2. Examples - HTML <html> <body> <h1>My First Heading</h1> <p>My first

    paragraph.</p> </body> </html> <html> <body> <h2>An Unordered List</h2> <ul> <li>Coffee</li> <li>Tea</li> </ul> </body> </html>
  3. Examples - CSS div { background-color: lightgrey; width: 300px; padding:

    50px; } h1 { font-size: 40px; } h2 { font-size: 30px; }
  4. Examples - SQL SELECT * FROM Customers WHERE Last_Name=”Sen” SELECT

    First_Name, Nickname FROM Friends WHERE Nickname like ‘%brain%’
  5. Examples - HTML val html = document.create.html { head {

    title("Hello world") } body { h1("h1Class"){ +"My header1" } } } https://github.com/Kotlin/kotlinx.html
  6. Examples - CSS val css = Stylesheet { a {

    width = 10.px color = 0xffffff hover { color = 0xf2cacf } } } https://github.com/olegcherr/Aza-Kotlin-CSS
  7. Examples - SQL val row = from(Friends) .where { Nickname

    like "brian" } .select(Friends.First_name, Friends.Nickname) https://github.com/orangy/squash
  8. Advantages There must be a reason DSL exists and developers

    write customs DSLs • Expressive • Readable
  9. Advantages There must be a reason DSL exists and developers

    write customs DSLs • Expressive • Readable • Concise - Less Boilerplate
  10. conference { name = "DevConfIN 19" location = "Bengaluru" room

    { roomNumber = 1 floor = 2 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } } room { roomNumber = 2 floor = 3 speaker { name = "Sayantika Banik" talkTitle = "Open Source" } } }
  11. Why Kotlin? Kotlin provides certain features that let us develop

    expressive and structured DSL • Lambda with Receiver • Lambda as method parameter • Extension function
  12. Lambda Lambdas are code blocks enclosed in curly braces. val

    multiply: (Int, Int) -> Int val multiply = { a: Int, b: Int -> a*b }
  13. Lambda Lambdas are code blocks enclosed in curly braces. val

    multiply: (Int, Int) -> Int val multiply = { a: Int, b: Int -> a*b } println(multiply(3,4))
  14. fun speaker(block: (SpeakerModel) -> Unit): SpeakerModel { val speaker =

    SpeakerModel() block(speaker) return speaker }
  15. fun speaker(block: (SpeakerModel) -> Unit): SpeakerModel { val speaker =

    SpeakerModel() block(speaker) return speaker }
  16. fun speaker(block: SpeakerModel.() -> Unit): SpeakerModel { val speaker =

    SpeakerModel() block(speaker) return speaker }
  17. fun speaker(block: SpeakerModel.() -> Unit): SpeakerModel { val speaker =

    SpeakerModel() block(speaker) return speaker } Receiver Lambda
  18. fun speaker(block: SpeakerModel.() -> Unit): SpeakerModel { val speaker =

    SpeakerModel() speaker.block() return speaker }
  19. val speaker = speaker({ name = "Subhrajyoti Sen" talkTitle =

    "Intro to Kotlin DSL" }) Extra bracket?
  20. Lambda as a Parameter fun modify(a: Int, lambda: (Int) ->

    Int) modify(3, someLambda) modify(3) { }
  21. room { roomNumber = 1 floor = 2 speaker {

    name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } }
  22. data class RoomModel( var roomNumber: Int = 0, var floor:

    Int = 99, var speaker: SpeakerModel? = null )
  23. fun room(block: RoomModel.() -> Unit): RoomModel { val room =

    RoomModel() room.block() return room }
  24. room { roomNumber = 1 floor = 2 speaker {

    name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } }
  25. room { roomNumber = 1 floor = 2 speaker {

    name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } }
  26. Extension Function A function that extends the functionality without modifying

    it fun Banner.isVisible(): Boolean { return this.visibility == Banner.VISIBLE }
  27. Extension Function A function that extends the functionality without modifying

    it fun Banner.isVisible(): Boolean { return this.visibility == Banner.VISIBLE } Base class
  28. Extension Function A function that extends the functionality without modifying

    it fun Banner.isVisible(): Boolean { return this.visibility == Banner.VISIBLE } Base class Extension function
  29. fun speaker(block: SpeakerModel.() -> Unit): SpeakerModel { val speaker =

    SpeakerModel() speaker.apply(block) return speaker }
  30. room { roomNumber = 1 floor = 2 speaker {

    name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } }
  31. val conference = conference { name = "DevConfIN 19" location

    = "Bengaluru" room { roomNumber = 1 floor = 2 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } } }
  32. data class ConferenceModel( var name: String = "", var location:

    String = "", var rooms: MutableList<RoomModel> = mutableListOf() )
  33. data class ConferenceModel( var name: String = "", var location:

    String = "", var rooms: MutableList<RoomModel> = mutableListOf() ) Create an empty mutable list
  34. fun conference(block: ConferenceModel.() -> Unit): ConferenceModel { val conference =

    ConferenceModel() conference.apply(block) return conference }
  35. conference { name = "DevConfIN 19" location = "Bengaluru" room

    { roomNumber = 1 floor = 2 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } } room { roomNumber = 2 floor = 3 speaker { name = "Sayantika Banik" talkTitle = "Open Source" } } }
  36. conference { name = "DevConfIN 19" location = "Bengaluru" room

    { roomNumber = 1 floor = 2 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } } }
  37. conference { name = "DevConfIN 19" location = "Bengaluru" room

    { roomNumber = 1 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" floor = 2 } } }
  38. conference { name = "DevConfIN 19" location = "Bengaluru" room

    { roomNumber = 1 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" floor = 2 } } }
  39. conference { name = "DevConfIN 19" location = "Bengaluru" room

    { roomNumber = 1 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" floor = 2 } } } Still inside the scope of room
  40. Scope Control - DSL Marker The lambda should be able

    to access only it’s nearest implicit receiver.
  41. Scope Control - DSL Marker The lambda should be able

    to access only it’s nearest implicit receiver. @DslMarker @Target(AnnotationTarget.TYPE) annotation class ConfDSL
  42. fun RoomModel.speaker(block: SpeakerModel.() -> Unit) { // ... } fun

    ConferenceModel.room(block: RoomModel.() -> Unit) { // ... } fun conference(block: ConferenceModel.() -> Unit): ConferenceModel { // ... }
  43. fun RoomModel.speaker(block: (@ConfDSL SpeakerModel).() -> Unit) { // ... }

    fun ConferenceModel.room(block: (@ConfDSL RoomModel).() -> Unit) { // ... } fun conference(block: (@ConfDSL ConferenceModel).() -> Unit): ConferenceModel { // ... }