코틀린 언어 용어 사전
DSLDSL · 연산자 오버로딩 · 중위 함수

수신자 있는 람다

람다 안에서 this가 특정 객체가 되는 문법. Compose·Gradle 같은 DSL의 토대다.

람다의 this가 특정 객체가 되는 함수 타입.

// A.() -> Unit  ←  A 가 수신자다
fun buildString(block: StringBuilder.() -> Unit): String =
    StringBuilder().apply(block).toString()

val s = buildString {
    append("안녕")      // this.append — this 가 StringBuilder 다
    append("하세요")
}

DSL을 만드는 방법

class HtmlBuilder {
    private val sb = StringBuilder()
    fun body(block: HtmlBuilder.() -> Unit) { sb.append("<body>"); block(); sb.append("</body>") }
    fun p(text: String) { sb.append("<p>$text</p>") }
    override fun toString() = sb.toString()
}

fun html(block: HtmlBuilder.() -> Unit) = HtmlBuilder().apply(block).toString()

val page = html {
    body {
        p("안녕하세요")     // 중첩된 스코프 안에서 그 객체의 함수를 바로 부른다
    }
}

중괄호 안에서 그 컨텍스트의 함수를 바로 부를 수 있다는 것이 DSL의 문법적 토대다.

@DslMarker — 스코프가 새는 것을 막는다

@DslMarker annotation class HtmlDsl

@HtmlDsl class HtmlBuilder { ... }
// 이제 중첩된 안쪽에서 바깥 스코프의 함수를 실수로 부를 수 없다

Compose가 이 문법의 결정체다

Column(modifier = Modifier.padding(16.dp)) {      // 이 람다의 수신자가 ColumnScope
    Text("제목")
    Spacer(Modifier.height(8.dp))
    Row {
        Text("왼쪽", modifier = Modifier.weight(1f))   // weight 는 RowScope 에만 있다
    }
}

weightRow·Column 안에서만 보이는 것이 수신자 스코프 덕분이다 — 잘못된 위치에서 쓰면 컴파일 에러가 난다.

연산자 오버로딩

data class Money(val amount: Long) {
    operator fun plus(other: Money) = Money(amount + other.amount)
    operator fun compareTo(other: Money) = amount.compareTo(other.amount)
}

Money(100) + Money(200)      // plus 가 불린다
Money(100) < Money(200)      // compareTo 가 불린다
// Compose 의 Modifier 체이닝도 확장 함수의 연속이다
Modifier.padding(8.dp).background(Color.Red).clickable { }

중위 함수

infix fun Int.to(other: Int) = Pair(this, other)
1 to 2                       // 점과 괄호 없이

mapOf("a" to 1, "b" to 2)    // 표준 라이브러리의 to 가 이것이다

조건: 멤버 함수이거나 확장 함수 · 파라미터가 정확히 하나 · 기본값 없음

남용의 경계

DSL 은 배우는 비용이 있다

  • 팀 전체가 자주 쓰는 구조에만 만든다
  • 한 번 쓰이는 곳에 DSL 을 만들면 순수한 부채다

연산자 오버로딩도 마찬가지 — 의미가 직관적일 때만 (Money + Money 는 OK, Money * User 는 아니다)

면접 함정

  • "수신자 있는 람다는 일반 람다와 같다"this가 달라 스코프 안의 함수를 직접 부를 수 있다.
  • "Compose는 특별한 컴파일러 마법" → 컴파일러 플러그인이 있긴 하지만, 문법의 뼈대는 수신자 있는 람다다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — DSL·연산자 오버로딩·중위 함수