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.

53b72671be580e70c9795c7eaf35ac12?s=128

Subhrajyoti Sen

August 02, 2019
Tweet

More Decks by Subhrajyoti Sen

Other Decks in Programming

Transcript

  1. Kotlin DSLs and How To Build Them Subhrajyoti Sen smallcase

  2. DSL??

  3. 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
  4. 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>
  5. Examples - CSS div { background-color: lightgrey; width: 300px; padding:

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

    First_Name, Nickname FROM Friends WHERE Nickname like ‘%brain%’
  7. Where’s my Kotlin?

  8. Examples - HTML val html = document.create.html { head {

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

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

    like "brian" } .select(Friends.First_name, Friends.Nickname) https://github.com/orangy/squash
  11. Examples - SQL https://github.com/arunkumar9t2/transition-x

  12. Advantages There must be a reason DSL exists and developers

    write customs DSLs
  13. Advantages There must be a reason DSL exists and developers

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

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

    write customs DSLs • Expressive • Readable • Concise - Less Boilerplate
  16. 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" } } }
  17. Why Kotlin? Kotlin provides certain features that let us develop

    expressive and structured DSL • Lambda with Receiver • Lambda as method parameter • Extension function
  18. val speaker = speaker { name = "Subhrajyoti Sen" talkTitle

    = "Intro to Kotlin DSL" }
  19. val speaker = speaker { name = "Subhrajyoti Sen" talkTitle

    = "Intro to Kotlin DSL" } Function
  20. data class SpeakerModel( var name: String = “”, var talkTitle:

    String = “” )
  21. fun speaker() { }

  22. fun speaker(): SpeakerModel { }

  23. fun speaker(): SpeakerModel { val speaker = SpeakerModel() }

  24. fun speaker(): SpeakerModel { val speaker = SpeakerModel() return speaker

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

    SpeakerModel() return speaker }
  26. Lambda

  27. Lambda Lambdas are code blocks enclosed in curly braces.

  28. Lambda Lambdas are code blocks enclosed in curly braces. val

    multiply: (Int, Int) -> Int
  29. Lambda Lambdas are code blocks enclosed in curly braces. val

    multiply: (Int, Int) -> Int val multiply = { a: Int, b: Int -> a*b }
  30. 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))
  31. fun speaker(block: (SpeakerModel) -> Unit): SpeakerModel { val speaker =

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

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

    "Intro to Kotlin DSL" })
  34. fun speaker(block: (SpeakerModel) -> Unit): SpeakerModel { val speaker =

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

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

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

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

    "Intro to Kotlin DSL" })
  39. val speaker = speaker({ name = "Subhrajyoti Sen" talkTitle =

    "Intro to Kotlin DSL" })
  40. val speaker = speaker({ name = "Subhrajyoti Sen" talkTitle =

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

    Int)
  42. Lambda as a Parameter fun modify(a: Int, lambda: (Int) ->

    Int) modify(3, someLambda)
  43. Lambda as a Parameter fun modify(a: Int, lambda: (Int) ->

    Int) modify(3, someLambda) modify(3) { }
  44. val speaker = speaker({ name = "Subhrajyoti Sen" talkTitle =

    "Intro to Kotlin DSL" })
  45. val speaker = speaker { name = "Subhrajyoti Sen" talkTitle

    = "Intro to Kotlin DSL" }
  46. room { roomNumber = 1 floor = 2 speaker {

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

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

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

    name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" } }
  50. But there’s a problem

  51. room { roomNumber = 1 floor = 2 speaker {

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

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

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

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

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

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

    speaker.apply(block) }
  58. fun RoomModel.speaker(block: SpeakerModel.() -> Unit) { val speaker = SpeakerModel()

    speaker.apply(block) }
  59. fun RoomModel.speaker(block: SpeakerModel.() -> Unit) { val speaker = SpeakerModel()

    speaker.apply(block) this.speaker = speaker }
  60. room { roomNumber = 1 floor = 2 speaker {

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

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

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

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

    ConferenceModel() conference.apply(block) return conference }
  65. fun ConferenceModel.room(block: RoomModel.() -> Unit) { val room = RoomModel()

    room.apply(block) this.rooms.add(room) }
  66. 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" } } }
  67. Not yet done

  68. conference { name = "DevConfIN 19" location = "Bengaluru" room

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

    { roomNumber = 1 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" floor = 2 } } }
  70. This actually compiles and works

  71. conference { name = "DevConfIN 19" location = "Bengaluru" room

    { roomNumber = 1 speaker { name = "Subhrajyoti Sen" talkTitle = "Intro to Kotlin DSL" floor = 2 } } }
  72. 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
  73. Scope Control - DSL Marker The lambda should be able

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

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

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

    fun ConferenceModel.room(block: (@ConfDSL RoomModel).() -> Unit) { // ... } fun conference(block: (@ConfDSL ConferenceModel).() -> Unit): ConferenceModel { // ... }
  77. To conclude, DSLs are awesome

  78. Questions? http://bit.ly/DevConfDSL

  79. Thank you SubhrajyotiSen iamsubhrajyoti