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.0m27.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.