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

Transcript

79 slides
  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 { // ... }

More Decks by Subhrajyoti Sen

Other Decks in Programming