고차 함수는 다른 함수를 인자로 받거나 함수를 반환하는 함수다. 코틀린에서는 람다나 함수 참조를 사용해 함수를 값으로 표현할 수 있다. 따라서 고차 함수는 람다나 함수 참조를 인자로 받거나 반환할 수 있다.

함수 타입

람다 인자의 타입을 어떻게 선언할 수 있는지 알아보자. 인자 타입을 정의하기 전에 더 단순한 경우로 람다를 로컬 변수에 대입하는 경우를 살펴보자. 코틀린의 타입 추론으로 인해 변수 타입을 지정하지 않아도 람다를 변수에 대입할 수 있다.

val sum = { x: Int, y: Int -> x + y }
val action = { println(42) }

컴파일러는 sum과 action이 함수 타입임을 추론한다. 각 변수의 타입을 직접 선언하면 다음과 같다.

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
val action: () -> Unit = { println(42) }

함수 타입을 정의하려면 함수 파라미터의 타입을 괄호 안에 넣고, 그 뒤에 화살표 →를 추가한 다음, 함수의 반환 타입을 지정하면 된다.

(Type, Type, …) → Type

Unit 타입은 의미 있는 값을 반환하지 않는 함수 반환 타입에 쓰는 특별한 타입이다. 그냥 함수를 정의할 때는 생략해도 되지만, 함수 타입을 선언할 때는 반환 타입을 반드시 명시해야 하므로 Unit을 생략할 수 없다.

변수의 함수 타입을 지정하면 람다의 파라미터 타입을 유추할 수 있다. 따라서 람다 식 안에서 파라미터 타입을 생략할 수 있다.

val sum: (Int, Int) -> Int = { x, y -> x + y }

다른 함수와 마찬가지로 함수 타입에서도 반환 타입을 널이 될 수 있는 타입으로 지정할 수 있다.

널이 될 수 있는 함수 타입 변수를 정의할 수도 있다. 함수의 반환 타입이 널인 것과 구분하기 위해 전체 함수 타입을 괄호로 감싸줘야 한다.

var funOrNull: ((Int, Int) -> Int?)? = null

함수 타입에서 파라미터 이름을 지정할 수도 있다. 파라미터 이름은 타입 검사 시 무시된다. 함수 타입의 람다를 정의할 때 파라미터 이름이 꼭 함수 타입 선언의 파라미터 이름과 일치하지 않아도 된다.

fun performRequest(
    url: String,
    callback: (code: Int, content: String) -> Unit,
) {
    /* .. */
}

fun main() {
    val url = "naver.com"
    performRequest(url) { code, content -> TODO() } // 함수 타입 선언시 정의한 파라미터 이름을 사용해도 되고
    performRequest(url) { code, page -> TODO() } // 원하는 다른 이름을 붙여도 된다.
}