liveBook · Manning

Domain-specific language

As explained in Kotlin in Action, 2E

A domain-specific language is a language designed to express operations and configurations within a particular problem domain.

A domain-specific language (DSL) expresses operations in one focused problem domain while leaving out general-purpose capabilities that domain does not need. SQL and regular expressions are familiar standalone DSLs: each uses compact, task-specific syntax rather than providing everything needed to build an application. DSLs are often declarative, describing a desired result while an execution engine handles the details. section 13.1.1

A Kotlin DSL is an internal language: ordinary Kotlin constructs are arranged to read like a specialized notation. It can make configuration, object construction, database access, HTML generation, tests, and build scripts more direct without losing static typing, compile-time error detection, or IDE support. section 13.0 section 13.1

Routing with a Kotlin DSL

Ktor uses nested blocks to declare HTTP routing: embeddedServer(Netty, port = 8000) { routing { get("/world") { call.respondText("Hello, world!") } route("/greet") { get { /* ... */ } post("/{entityId}") { /* ... */ } } } } routing, get, post, and route look like language keywords, but they are framework-defined Kotlin constructs. The nesting groups related routes and carries routing context into each block, replacing annotation- or XML-based configuration. section 1.3.1

The boundary between a DSL and a clean API is not rigid. A regular command-query API typically calls methods independently; a DSL adds grammar, so several calls form one meaningful operation. Kotlin commonly creates that structure with lambdas with receivers, chained calls, infix calls, operator conventions, and builder functions. The type checker constrains which pieces can be combined. section 13.1 section 13.1.3

dependencies {                            #1
    testImplementation(kotlin("test"))
    implementation("org.jetbrains.exposed:exposed-core:0.40.1")
    implementation("org.jetbrains.exposed:exposed-dao:0.40.1")
}

The dependencies block shows grammar and shared context: dependency declarations stay inside dependencies instead of repeatedly naming project.dependencies. This reduces repetition compared with a command-query API while retaining typed Kotlin calls. section 13.1.3

Readable test assertions

An infix DSL can turn a conventional assertion into a sentence-like expression: s should startWith("K") Here, should is an infix extension function and startWith produces a matcher. The notation reduces punctuation, but an invalid combination still fails at compile time because the DSL remains statically typed. section 13.4.1

How Kotlin in Action, 2E covers it

How deep it goes
Defines DSLs, contrasts them with APIs, and explains grammar through nesting, chaining, and infix assertions.
Examples
Uses Ktor HTTP routes, Gradle dependencies, SQL, regular expressions, HTML builders, and Kotest matchers.
What it assumes
Builds on briefly introduced lambdas with receivers and develops the invoke convention in detail.
What you can do after
Build it from scratch by combining Kotlin syntax features into typed, structured APIs with domain-specific grammar.
How it connects
Connects DSLs to APIs, lambdas with receivers, invoke convention, infix calls, operator overloading, and static typing.

Also explained in

Compare how 10 books explain domain-specific language →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kotlin in Action, 2E. The section links point to the parts of the book it draws on.