반응형

함수형 프로그래밍이란?

함수형 프로그래밍은 절차 지향, 객체지향, 관점 지향 등과 같은 하나의 프로그래밍적 패러다임(프로그램을 구성하는 스타일)입니다.

함수형 프로그래밍의 본질적인 핵심은 표현식으로 데이터를 변환하는 것이고, (이상적으로는) 표현식에 부수효과(side-effect)가 없어야 합니다.

"함수형"이라는 이름에 걸맞게 함수형 프로그래밍은 수학 함수의 컨셉을 기반으로 합니다.

 

수학 함수는 입력과 출력 집합 사이의 관계를 정의한다. 각 입력은 하나의 출력만 합니다.

예를 들어, 함수 f(x) = x^2에서 f(5)는 언제나 25 입니다.

 

프로그래밍 언어에서 파라미터가 있는 함수를 호출하면 언제나 같은 값을 반환하도록 보장하는 방법은 가변적인 상태에 접근하는 것을 피하게 하고, 이는 멀티쓰레딩 환경에서 동기화 문제로부터 벗어나게 해 줍니다.

 

예를 들어, 아래와 같은 함수는 같은 인풋에 대해 항상 동일한 결과를 반환합니다.

fun f(x: Int) : Int {
    return x * x
}

f 함수는 외부 상태에 접근하지 않고 파라미터에 대해서만 처리를 하기 때문에 f(5) 호출은 언제나 25를 반환할 것입니다.

 

fun main(args: Array<String>) {
    var i = 0
    
    fun g(x: Int) Int {
        return x * i
    }
    
    println(g(5)) // 0 출력
    
    i = 5
    println(g(5)) // 25 출력
}

반면에 위의 g 함수는 가변적인 상태 i에 따라 같은 입력 값임에도 다른 값을 반환합니다.

 

사실상 이런 부수 효과를 완전히 배제하면서 프로그래밍하는 것은 쉽지 않습니다. 그래서 "이런 부수 효과가 나타나는 모든 함수는 다 함수형스럽지 않아!" 라고는 할 수 없지만, 적어도 함수형스럽게 짠다는 게 뭔지 알고 그를 '지향'하는 것이 함수형 프로그래밍이 아닐까 싶습니다.

그렇다면 함수형 프로그래밍 스타일은 어떤 이점을 갖고 있을까요?

 

함수형 프로그래밍 스타일은 다음과 같은 이점을 제공합니다.

  • 코드는 읽기 쉽고 테스트하기 쉽다.
    외부 가변 상태에 의존하지 않는 함수는 더 쉽게 접근할 수 있고, 더 쉽게 증명할 수 있습니다.
  • 상태(state)와 부수 효과(side effect)가 신중하게 계획된다.
    상태 관리를 코드에 개별적으로 특정 위치로 제한하는 것은 유지와 리팩토링을 쉽게 만듭니다.
  • 동시성이 좀 더 안전해지며 더 자연스러워진다.
    가변 상태가 없다는 것은 동시성 코드가 코드 주변에서 잠금이 적거나 필요 없다는 것을 뜻합니다.

 

일급 함수(first-class functions)

함수형 프로그래밍의 가장 기본적인 컨셉은 일급 함수(first-class functions) 입니다.

일급 함수를 지원하는 프로그래밍 언어는 함수를 다른 타입으로 취급합니다.

즉, 함수를 변수, 파라미터, 리턴, 일반화 타입 등으로 사용할 수 있게 합니다.

 

하나하나 예시를 살펴보자면 우선 아래와 같이 변수처럼 함수를 저장할 수 있습니다.

val capitalize = { str: String -> str.capitalize() }

fun main(args: Array<String>) {
    println(capitalize("hello, world!")) // 결과 : HELLO, WORLD!
}

코틀린 공식 문서에서는 이런 종류의 함수를 '람다(lambda)'라고 부릅니다.

 

사실 이 람다 함수는 코틀린 컴파일러에 의해 Funtion1<P, R> 객체로 변환이 되는데, 내부적으로 다음 코드와 동일한 기능을 합니다.

val capitalize = object : Function1<String, String> {
    override fun invoke(p1: String): String {
        return p1.capitalize()
    }
}

이렇듯 코틀린에서 람다 함수는 결국엔 객체로 관리가 되기 때문에 다른 함수의 파라미터로도 사용할 수 있습니다.

 

다음 예제를 살펴보겠습니다.

fun transform(str: String, fn: (String) -> String): String {
    return fn(str)
}

위와 같이 transform 함수에서 두 번째 파라미터 타입으로 (String) -> String 형식의 함수를 파라미터로 받겠다고 정의를 해놓으면 앞서 만들었던 capitalize에 저장된 함수를 파라미터로 사용할 수 있습니다.

fun main(args: Array<String>) {
    println(transform("kotlin", capitalize)) // 결과 : KOTLIN
}

그리고 transform 함수와 같이 다른 함수를 파라미터로 사용하거나 리턴 값으로 사용하는 함수를 고차 함수(higher-order function)이라고 합니다.

 

 

반응형
반응형