# DSL 만들기
영역 특화 언어를 사용해 코틀린다운 API를 설계하는 방법을 설명
코틀린 DSL
- 간결한 구문을 제공하는 기능과, 그런 구문을 확장해서 여러 메서드 호출을 생성하는 구조를 만들어낼 수 있음.
- 결과적으로 DSL은 개별 메서드 호출로 구성된 API보다 훨씬 더 표현력이 풍부하고 편하게 작업할 수 있다.
- 코틀린 DSL도 온전히 컴파일 시점에 타입이 정해진다. 따라서 컴파일 시점 오류 감지, IDE 지원 등 모든 정적 타입 지정 언어의 장점을 누릴 수 있다.
- 코틀린 DSL에서는 보통 람다를 중첩시키거나 메서드 호출을 연쇄시키는 방식으로 구조를 만든다.
DSL
- 영역 특화 언어 : 특정 과업 또는 영역에 초점을 맞추고 그 영역에 필요하지 않은 기능을 없앤 영역 특화 언어를 구분해 왔다.(선언적)
- sql, html
- 범용 프로그래밍 언어: 모든 문제를 충분히 풀 수 있는 기능을 제공(명력적)
- java, c
- DSL을 범용 언어로 만든 호스트 애플리케이션과 함께 조합하기가 어렵다는 점이다. DSL은 자체 문법이 있기 대문에 다른 언어의 프로그램 안에 직접 포함시킬 수 가 없다 따라서 DSL로 작성한 프로그램을 다른
언어에서 호출하려면 DSL 프로그램을 별도의 파일이나 문자열 리터럴로 저장해야한다.
```kotlin
val sql = """
select ....
"""
일반 구문 | 간결한 구문 | 사용한 언어 특성 |
---|---|---|
StringUtil.capitalize(s) | s.capitalize | 확장 함수 |
1.to("one") | 1 to "one" | 중위 호출 |
set.add(2) | set += 2 | 연상자 오버로딩 |
map.get("key") | map["key"] | get 메서드에 대한 관례 |
file.use({ f -> f.read()}) | file.use{ it.read()} | 람다를 괄호 밖으로 빼내는 관례 |
sb.append("yes") sb.append("no") | with (sb) {sb.append("yes") sb.append("no")} | 수신 객체 지정 람다 |
내부 DSL
수신 객체 지정 람다와 확장 함수 타입
fun buildString(
builderAction: (StringBuilder) -> Unit
): String {
val sb = StringBuilder()
builderAction(sb)
return sb.toString()
}
fun main() {
val s = buildString {
it.append("Hello,")
it.append("World")
}
println(s)
}
fun buildString(
builderAction: (StringBuilder).() -> Unit
): String {
val sb = StringBuilder()
sb.builderAction()
return sb.toString()
}
fun main() {
val s = buildString {
append("Hello,")
append("World")
}
println(s)
}
String.(Int, Int) -> Unit
class Greeter(val greeting: String) {
operator fun invoke(name: String){
println("$greeting, $name!")
}
}
fun main(){
val bavarianGreeter = Greeter("Servus")
bavarianGreeter("Dmitry")
}
interface Function2<in P1, in P2, out R> {
operator fun invoke(p1: P1, p2: P2): R
}