본문 바로가기

코틀린

코틀린: 함수형 프로그래밍과 일급 객체

함수형 프로그래밍에서 사용하는 일급 객체(first class citizen)란 용어가 낯선가요? 혹시 이코노미석, 비지니스석처럼 구분하는 느낌이 들어 거북하지 않았나요? 사실 알고보면 아주 간단한 개념입니다. 일급 객체란 함수형 프로그래밍에서는 함수를 변수(=값)처럼 다룰 수 있다는 뜻입니다. 자세한 내용은 쉽게 다가가는 최신 프로그래밍: 코틀린 - 3.2 함수형 프로그래밍을 참고하기 바랍니다.

아래 예에서 두 수를 더하는 함수 sum()을 정의하고 있습니다. sum()의 형식 인자 a, b는 Int 타입 변수(또는 값)를 전달받을 수 있습니다. 함수 sum()은 덧셈 결과인 Int 타입 변수 c를 반환합니다. 여기까지는 일반 함수를 사용했습니다.

fun sum(a:Int, b:Int): Int {
    val c: Int = a + b
    return c
}

fun main() {
    println(sum(1, 2))
}

 

함수 sum()의 형식 인자로 Int 타입의 값(또는 변수) 대신 함수를 전달하면 어떻게 될까요? 덧셈 함수 firstCitizen()을 정의합니다. sum()의 형식 인자 타입을 함수 firstCitizen()에 맞게 함수 타입(Int 타입 형식 인자 2개, 반환 타입은 Int)으로 선언합니다. 이제 firstCitizen()을 sum()의 형식 인자로 전달할 수 있습니다. 함수를 변수(또는 값)처럼 사용할 수 있을 때, 이를 함수형 프로그래밍이라 부릅니다. 이렇게 함수를 변수처럼 사용할 수 있으면 이 함수를 일급 객체라고 부릅니다.

fun firstCitizen(a: Int, b: Int) = a + b

fun sum(op: (Int, Int) -> Int): Int {
    val c = op(1, 2)
    return c
}

fun main() {
    println(sum(::firstCitizen))
}

 

함수를 형식 인자로 전달하려면 함수 참조 연산자(::)를 사용해야 합니다. 람다 식을 전달하면 함수 참조 연산자가 필요 없습니다.

fun sum(op: (Int, Int) -> Int): Int {
    val c: Int = op(1, 2)
    return c
}

fun main() {
    println(sum { x: Int, y: Int -> x + y })
}

 

함수를 형식 인자로 전달하는 과정이 복잡해 보이지만, 형식 인자로 전달된 함수를 호출할 때 비로소 연산을 실행합니다. 이런 상황을 이해하기 쉽도록 op(1, 2) 대신 op.invoke(1, 2)로 쓸 수 있습니다.

val c = op.invoke(1, 2)

 

그런데, 함수를 형식 인자로 전달하면 뭐가 좋을까요? 다양한 함수를 형식 인자로 전달할 수 있습니다. 위 예에서는 덧셈 함수만 전달했지만, 곱셈 함수, 나눗셈 함수 등 다양한 함수를 전달할 수 있죠... 레고 블록 조립할 때 생각해 보세요. 기본적인 블록들을 결합해 해적선, 마법의 성 등을 만들죠. 함수형 프로그램도 마찬가지입니다. 똘똘한 함수 조각들을 만들어 놓고, 이 함수 조각들을 조합해 더 복잡한 함수를 만들 수 있죠. 이런 접근 방식이 바로 함수형 프로그래밍이 등장하게 된 배경입니다.