DSL·연산자 오버로딩·중위 함수 — 읽기 좋은 코드를 만드는 문법
코틀린은 코드를 자연어처럼 읽히게 하는 문법을 갖췄다 — 연산자 오버로딩(+·[]를 내 타입에), 중위 함수(1 to 2), 그리고 수신자가 있는 람다로 만드는 DSL(도메인 특화 언어). Jetpack Compose의 UI 선언, Gradle Kotlin DSL, Coroutine 빌더가 모두 이 문법들의 산물이다. (출처: Kotlin Docs — Operator overloading / Type-safe builders / Kotlin in Action 2e Ch 13.)
연산자 오버로딩 — 기호를 내 타입에
operator 키워드를 붙인 약속된 이름의 함수로 연산자를 내 타입에 정의한다.
data class Vec(val x: Int, val y: Int) {
operator fun plus(o: Vec) = Vec(x + o.x, y + o.y) // a + b
operator fun get(i: Int) = if (i == 0) x else y // a[0]
operator fun compareTo(o: Vec) = ... // a < b
}
Vec(1,2) + Vec(3,4) // Vec(4,6) — plus 호출
vec[0] // get(0)
주요 매핑 — plus/minus/times(+ - *), get/set([]), invoke(obj()), contains(in), compareTo(< > <= >=), rangeTo(..), inc/dec(++ --). 남용하면 오히려 헷갈리니 — 의미가 직관적일 때만(벡터·행렬·금액·기간 등).
중위 함수 — 점·괄호 없이
infix 함수는 a.foo(b)를 a foo b로 쓰게 한다(수신자·인자 하나).
infix fun Int.times(s: String) = s.repeat(this)
2 times "Bye " // "Bye Bye "
mapOf("a" to 1) // to 도 infix 함수 (Pair 생성)
if (x in 1..10) ... // .. 는 rangeTo 연산자
수신자 있는 람다 — DSL의 핵심
DSL을 가능케 하는 핵심 문법이 수신자가 있는 람다(lambda with receiver) — A.() -> Unit 타입의 람다는 블록 안에서 A의 멤버를 this 없이 부른다. apply { }가 바로 이것이다.
class HtmlBuilder {
val sb = StringBuilder()
fun p(text: String) { sb.append("<p>$text</p>") }
}
// 수신자 있는 람다를 받는 빌더 함수
fun html(block: HtmlBuilder.() -> Unit): String =
HtmlBuilder().apply(block).sb.toString()
val page = html { // 이 블록 안에서 this == HtmlBuilder
p("안녕") // p()를 this 없이 호출
p("DSL")
}
이 패턴으로 중첩 구조를 자연스럽게 표현한다 — Jetpack Compose의 Column { Text(...) }, Gradle Kotlin DSL의 dependencies { implementation(...) }, 코루틴의 launch { }가 모두 수신자 있는 람다다.
@DslMarker로 중첩된 수신자들이 섞이는 걸 막아(바깥 수신자의 멤버를 안쪽에서 실수로 부르지 못하게) DSL을 안전하게 만든다.
Compose가 곧 DSL의 결정체
@Composable
fun Greeting(name: String) {
Column(Modifier.padding(16.dp)) { // 수신자 람다 → 중첩 UI
Text("안녕, $name")
Button(onClick = { /* ... */ }) { Text("확인") }
}
}
위 코드가 연산자(16.dp는 확장 프로퍼티)·trailing 람다·수신자 람다의 종합이다 — [andsys] Compose 장에서 다시 다룬다. 코틀린의 문법 설계가 Compose 같은 선언형 UI를 가능케 했다.
정리 — 문법이 표현력을 만든다
코틀린의 연산자 오버로딩·중위 함수·수신자 람다는 코드를 도메인 언어처럼 읽히게 한다. 그 결정체가 Compose UI·Gradle DSL·코루틴 빌더다. 직접 DSL을 자주 만들진 않더라도 — 이 문법들을 알면 Compose·Gradle 코드가 왜 그렇게 생겼는지가 바닥부터 보인다.
한 줄 요약 —
operator fun으로+·[]·in·..을 내 타입에(직관적일 때만).infix로a to b. 수신자 있는 람다(A.() -> Unit)가 DSL의 핵심 —apply·ComposeColumn { }·Gradledependencies { }가 전부 이것.@DslMarker로 중첩 안전.
(출처: Kotlin Docs — Operator Overloading / Type-Safe Builders / Kotlin in Action 2e Ch 13 / Compose mental model.)