ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 11-3. 도메인 특화 언어 : 고차 함수와 DSL
    Study(종료)/Kotlin 22.09.13 ~ 12.18 2022. 12. 2. 15:35

    11장

    11-1-1. 도메인 특화 언어 : 연산자 오버로딩(1~5)

    11-1-2.  도메인 특화 언어 : 연산자 오버로딩(6~8)

    11-2.  도메인 특화 언어 : 위임 프로퍼티

    11-3. 도메인 특화 언어 : 고차 함수와 DSL


    11-3. 도메인 특화 언어 : 고차 함수와 DSL

     

    1) 중위 함수를 사용해 플루언트 DSL 만들기

     

    중위 함수를 이용하는 플루언트(fluent) API를 만들어 보자.

    컬렉션 데이터에 대한 질의에 사용할 수 잇는 SQL과 비슷한 문법을 사용하는 간단한 DSL을 정의할 예정이다

    val nums = listOf(2, 8, 9, 5, 3, 5)
    val query = from(nums) where( it > 3 ) select { it * 2 } orderBy { it }
    println(query.items.toList)

    위와 같은 방식으로 코드를 작성하도록 허용하고 싶다는 의미이다(C#의 LINQ와 유사)

     

    기본적으로 만들 질의는 아래 요소로 구성된다.

    1. from 절은 대상 컬렉션을 지정

    2. from 다음으로 where절이 올 수 있다. 이 절은 걸러낼 조건을 기술

    3. where 다음으로 select 절이 올 수 있다. 이 절은 원래 데이터를 출력값으로 매핑

    4. select 절이 있는 경우, 원하는 경우 orderBy 절을 추가 가능.

       이 절은 결과의 순서를 정할 때 사용할 키를 지정

     

    위와같은 API를 구현하려면, 먼저 질의의 중간 구조를 표현하는 몇 가지 클래스를 정의한다.

    대부분의 중간 구조는 원래의 컬렉션이나 걸러낸 결과 등과 같은 일종의 데이터 집합을 표현하므로,

    1. 원소의 시퀸스를 돌려주는 기능을 제공하는 공통 인터페이스 ResultSet을 정의해야 한다.

    2. 이후 질의 구성 요소를 표현하는 클래스 From, Where, Select, OrderBy를 정의하고.

    3. DSL의 요구 사항에 맞게 이들을 엮어줄 중위 연산자 함수 infix 함수를 정의한다.

    4. 마지막으로 from() 함수를 정의하면 정상적으로 동작하는것을 볼 수 있다

    interface ResultSet<out T>{
        val items: Sequence<T>
    }
    
    class From<out T>(private val source: Iterable<T>) : ResultSet<T> {
        override val items: Sequence<T>
            get() = source.asSequence()
    }
    
    class Where<out T>(
        private val from: ResultSet<T>,
        private val condition: (T) -> Boolean
    ) : ResultSet<T> {
        override val items: Sequence<T>
            get() = from.items.filter(condition)
    }
    
    class Select<out T, out U>(
            private val from: ResultSet<T>,
            private val output: (T) -> U
    ) : ResultSet<U>{
        override val items: Sequence<U>
            get() = from.items.map(output)
    }
    
    class OrderBy<out T, in K : Comparable<K>>(
            private val select: ResultSet<T>,
            private val orderKey: (T) ->K
    ) : ResultSet<T>{
        override val items: Sequence<T>
            get() = select.items.sortedBy(orderKey)
    }
    
    infix fun <T> From<T>.where(condition: (T) -> Boolean) =
            Where(this, condition)
    // from 뒤에 where이 올 수 있음
    
    infix fun <T, U> From<T>.select(output: (T) -> U) =
            Select(this, output)
    infix fun <T, U> Where<T>.select(output: (T) -> U) =
            Select(this, output)
    // from이나 where 뒤에 select가 올 수 있음
    
    infix fun <T, K : Comparable<K>> Select<*, T>.orderBy(
            orderKey: (T) -> K
    ) = OrderBy(this, orderKey)
    
    fun <T>from(source: Iterable<T>) = From(source)
    
    
    fun main() {
        val nums = listOf(2, 8, 9, 1, 3, 6, 5)
        val query = from(nums) where { it > 3 } select { it * 2 } orderBy { it }
        println(query.items.toList())
        // [10, 12, 16, 18]
    }

    이번주는 일이 있어서 스터디도 못했고, 마무리도 좀 늦게 했다.급하게 작성하느라 꼼꼼히 작성하지 못한 감도 있는데, 다음 내용은 좀 더 자세히 작성하도록 노력해봐야겠다.

     

    다음장은 자바 상호 운용성이다.코틀린과 자바는 생각보다 많은 연관이 있는 것 같다.스터디를 할 때도 동작 원리가 궁금하면 디컴파일해 자바 코드를 보는 경우가 꽤나 많다.과연 어떤 내용일지 작성하면서 좀 더 깊게 알아봐야겠다.

    반응형

    댓글

Designed by Tistory.