Save 37% off PRO during our Black Friday Sale! »

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

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