Domain Specific Language in Kotlin: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
Line 115: Line 115:


===Infix Notation===
===Infix Notation===
With infix we a provide a more readable experience where the periods and bracket of an extension method can be omitted.
<syntaxhighlight lang="kotlin">


==Implementation Technics==
==Implementation Technics==

Revision as of 01:04, 13 April 2021

Introduction

Approaches to Extending Code

There may three options for extending a feature

  • Change the Object Model

This has imperative code and therefore you will be forced to abstract extend, abstract extend. You be able to build quickly but it does not scale.

  • External DSL (e.g. JSON)

E.g. Use json to describe the new features. You will need to extend the parser and write the code. Build will be slow but the scalability will be great

  • DSL in Kotlin

Because it is just kotlin building will be quicker and it will scale hmmmmmm

Attributes of DSL Code

  • Language Nature Code is meaningful and has a fluid nature
  • Domain Focus DSL is focus one problem only
  • Limited Expressiveness Supports only what it needs to to accomplished its task


Imperative vs Declarative

val castle = Castle()
val towerNE = Tower()
val towerSE = Tower()
val towerNW = Tower()
val towerSW = Tower()
val keep = Keep()

keep.connectTo(towerNE)
keep.connectTo(towerSE)
keep.connectTo(towerNW)
keep.connectTo(towerSW)

DSL Restricts the syntax to allow better IDE support and keep focus

castle {
   keep {
          to("sw")
          to("nw")
          to("se")
          to("nw")
        }
}

val castle = Castle()
val towerNE = Tower()
val towerSE = Tower()

Kotlin Language Features

Lambda with Receiver Invoke

A lambda with a receiver allows you to call methods of an object in the body of a lambda without any qualifiers. It is similar to the typed extension function but this time, for function types. The idea is similar to object initializers in C# but is extended to functions and in a declarative way.

We pass an object (StringBuilder) with an attibute (String) and a function to use with the two.

fun encloseInXMLAttribute(
  sb : StringBuilder, 
  attr : String, action : 
  (StringBuilder) -> Unit) : String {
    sb.append("<$attr>")
    action(sb)
    sb.append("</$attr>")
    return sb.toString()
}

// When a lambda expression is at the end of the parameter list, you can take it out of the parentheses during invocation.
val xml = encloseInXMLAttribute(StringBuilder(), "attr") {
    it.append("MyAttribute")
}

print(xml)

Operator Overloading

Property Override

Extension Functions

Existing Extension Methods

There are many extensions already in the language for Kotlin. In this case languages is the receiver as it thing apply acts on An example of apply is shown below.

  val languages = mutableListOf<String>()
  languages.apply { 
      add("Java")
      add("Kotlin")
      add("Groovy")
    add("Python")
  }.apply {
      remove("Python")
 }

This could have been written

  val languages = mutableListOf<String>()
  languages.add("Java");
  languages.add("Kotlin");
  languages.add("Groovy");
  languages.add("Python");
  languages.remove("Python");

Writing Out Own

We just need to specify the receiver class followed by a period. Here is an extension to String

fun String.escapeForXml() : String {
  return this
    .replace("&", "&amp;")
    .replace("<", "&lt;")
    .replace(">", "&gt;")
}

Generics

We can use generics to write extensions and the compiler will help.

fun <T> T.concatAsString(b: T) : String {
    return this.toString() + b.toString()
}

5.concatAsString(10) // compiles
"5".concatAsString("10") // compiles
5.concatAsString("10") // doesn't compile

Infix Notation

With infix we a provide a more readable experience where the periods and bracket of an extension method can be omitted. <syntaxhighlight lang="kotlin">

Implementation Technics

Function Sequencing

Function Chaining

Symbol Table

Nested Builder

Context Variables