Domain-Specific Languages

27.1 DSL Building Blocks

Optional parentheses and semicolons send email to "x" reads like prose Command chains

move left by 10 at speed 5 → move(left).by(10).at(speed: 5)-style chains deploy app: "web", env: "prod" Closures + delegate Nested configuration blocks money + tax, task1 >> task2

Custom base scripts and compiler customizers Pre-imported vocabulary, sandboxing Category/extension methods 3.days.ago, 10.km IDE and type-checking support

27.2 Command Chain DSL

def move(direction) {
    [by: { distance ->
        [at: { speed -> println "Moving $direction by $distance at $speed" }]
    }]
}
def left = "LEFT"
move left by 10 at "fast"          // → move(left).by(10).at("fast")

def please(action) { [the: { what -> [with: { tool -> println "$action the $what with $tool" }] }] }
please "paint" the "fence" with "brush"

27.3 Configuration DSL with Delegation

class ServerSpec {
    String host = "0.0.0.0"
    int port = 80
    List<String> routes = []
    void host(String h) { host = h }
    void port(int p) { port = p }
    void route(String path) { routes << path }
    void ssl(@DelegatesTo(SslSpec) Closure cl) { def s = new SslSpec(); cl.delegate = s; cl(); println "SSL: $s.cert" }
}
class SslSpec { String cert; void cert(String c) { cert = c } }

def server(@DelegatesTo(value = ServerSpec, strategy = Closure.DELEGATE_FIRST) Closure cl) {
    def spec = new ServerSpec()
    cl.delegate = spec
    cl.resolveStrategy = Closure.DELEGATE_FIRST
    cl()
    spec
}

def s = server {
    host "localhost"
    port 8443
    route "/api"
    route "/health"
    ssl { cert "/etc/cert.pem" }
}
println "${s.host}:${s.port} ${s.routes}"

27.4 Units DSL with Extension Methods

class Distance {
    double meters
    Distance plus(Distance o) { new Distance(meters: meters + o.meters) }
    String toString() { "${meters}m" }
}
Number.metaClass.getKm = { -> new Distance(meters: delegate * 1000) }
Number.metaClass.getM = { -> new Distance(meters: delegate) }
println 2.km + 300.m           // 2300.0m

27.5 Real-World DSL Examples

Gradle (dependencies { implementation 'x:y:1.0' }), Jenkins Pipeline (pipeline { stages { stage('Build') { steps { sh 'make' } } } }), Spock (given:/when:/then: blocks, data tables), Grails GORM (Book.findAllByTitleLike(...), criteria DSL), the Nextflow workflow DSL, Geb ($("div.result").text()), Ratpack handlers, Micronaut config.