Sobes.tech
Middle+

Czym są języki specyficzne dla domeny (DSL) i jak je tworzyć w Kotlin?

sobes.tech AI

Odpowiedź od AI

DSL (Domain Specific Language) to mały język, specjalizowany dla konkretnej dziedziny lub zadania, w odróżnieniu od języków ogólnego przeznaczenia (GPL - General Purpose Language). Umożliwia wyrażanie rozwiązań problemów w terminach tej dziedziny, czyniąc kod bardziej czytelnym, zrozumiałym i wyrazistym dla ekspertów w danej dziedzinie.

Kotlin jest bardzo odpowiedni do tworzenia wewnętrznych DSL dzięki obsłudze takich funkcji jak:

  • Wyrażenia lambda z receiverem: Pozwalają definiować bloki kodu wykonywane w kontekście określonego obiektu, co ułatwia tworzenie struktur hierarchicznych i konfiguracji.
  • Funkcje rozszerzające: Pozwalają dodawać nowe funkcje do istniejących klas bez ich modyfikacji, co jest przydatne do rozszerzania funkcjonalności standardowych klas lub bibliotek zewnętrznych do użytku w DSL.
  • Funkcje infix: Pozwalają wywoływać funkcje bez kropki i nawiasów, co sprawia, że kod jest bardziej naturalny.
  • Operatory: Można przeciążać operatory (+, *, [] itd.), aby tworzyć bardziej wyrazistą składnię.
  • Użycie operatora invoke: Pozwala wywołać obiekt jak funkcję, co może być użyte do tworzenia builderów lub konfiguratorów.
  • Typowo bezpieczni budowniczowie (type-safe builders): Połączenie lambd z receiverem i rozszerzeń, umożliwiające tworzenie strukturalnych i bezpiecznych w typie API.

Przykład tworzenia prostego DSL do budowania HTML:

import kotlinx.html.*
import kotlinx.html.stream.appendHTML
import java.io.StringWriter

fun html(block: HTML.() -> Unit): String {
    val writer = StringWriter()
    writer.appendHTML().html(block)
    return writer.toString()
}

fun body(block: BODY.() -> Unit) {
    BLOCK.BODY()(block)
}

fun p(text: String) {
    BLOCK.P() {
        +text
    }
}

Użycie tego DSL:

fun main() {
    val result = html {
        body {
            p("Witaj, świecie!")
            p("To jest mój pierwszy DSL.")
        }
    }
    println(result)
}

Wynik wykonania:

<html><body><p>Witaj, świecie!</p><p>To jest mój pierwszy DSL.</p></body></html>

Tutaj lambda block: HTML.() -> Unit jest wykonywana w kontekście obiektu HTML, co pozwala na bezpośrednie wywoływanie metod body. Funkcja p dodaje akapit z tekstem, korzystając z przeciążonego operatora unarnego +.