Deep Dive: Functional Programming(함수형 프로그래밍)
함수형 프로그래밍에 대해 다루기 전에, 잠시 다양한 프로그래밍 방식을 배워보자. 근래의 프로그래밍 방식은 다음과 같이 분류할 수 있다.
명령형 프로그래밍: 프로그램의 상태와 상태를 변경하는 구문의 관점에서 연산을 설명하는 방식. 즉, What이 아니라 How에 집중한다.
선언형 프로그래밍: 프로그램이 무엇과 같은 지를 설명하는 방식. 즉, How가 아니라 What에 집중한다.
함수형 프로그래밍이란 이렇게 모든 것을 순수 함수로 나누어 문제를 해결하는 기법이다. 그렇다면 함수로 나누어 문제를 해결하는 것이 왜 중요한 프로그래밍 패러다임이 되었을까? 기존의 프로그래밍 패러다임을 기반으로 개발했던 개발자들은 소프트웨어의 크기가 커짐에 따라 복잡하게 엉킨 스파게티 코드
를 유지 보수하는 것이 힘들다는 것을 깨닫게 되었다. 함수형 프로그래밍은 이를 해결하기 위해 등장한 패러다임으로, 함수 단위의 코드 재사용이 굉장히 용이하며, 불변성을 지향하기 때문에 프로그램의 동작을 예측하기 쉬워진다는 장점으로 인해 유명해졌다.
전반적인 소개를 마쳤으니, 이제 함수형 프로그래밍의 자세한 특징에 대해 알아보자. 위에서 언급한 ‘순수 함수’, ‘불변성’ 등 뿐만 아니라 다양한 특징들에 대해 살펴보도록 하겠다.
순수 함수란 무엇일까? 우선 함수는 동일한 입력에는 항상 같은 값을 반환하는 것을 뜻한다. 이중 함수의 실행이 프로그램의 실행에 영향을 미치지 않으며, 함수 내부에서 인자의 값을 변경하거나 프로그램 상태를 변경하는 부수 효과(Side Effect)가 없는 것을 순수 함수라고 말한다. 다음과 같은 예시를 보도록 하자.
fun add(a: Int, b; Int): = a + b
이때 위의 add는 순수 함수가 된다. 언제 어느 상황에서 실행해도 add(1+2)는 항상 3을 리턴하고, 외부 상태의 변경이 이루어지지 않기 때문이다. 즉, 메모리 어딘가에 저장되어 있던 1과 2라는 값은 변경되지 않은 상태로 그대로 남아있다. add 함수를 실행해도 메모리에 저장되어 있던 1과 2라는 값은 변경되지 않는다(Call By Value). 이처럼, C언어와 달리 Kotlin에서는 포인터 주소를 활용하지 않기 때문에 주소 참조(Call By Reference)가 아닌 값을 복사하여 전달하는 값에 의한 호출(Call By Value)이 일반적으로 일어난다.
여기서 부수효과(Side Effect)란 외부의 상태를 변경하는 것 또는 함수로 들어온 인자의 상태를 직접 변경하는 것을 의미한다. 구체적으로는 다음과 같은 변화를 의미한다.
함수형 프로그래밍에서의 데이터는 변하지 않는 불변성을 유지해야 한다. 순수 함수라는 개념과 불변성이라는 특징은 항상 같이 갈 수 밖에 없다. 수학의 세계에서 생각해보면, 수학에서는 어떤 상태
라는 것이 존재하지 않는다(어떤 한 상태에 놓여있다는 말은 다른 상태에 놓일 수 있다는 개념을 내포하므로). 예를 들어 (2, 3) 좌표에 있는 점은 x=2, y=3이라는 불변성
을 가지고 있다. 이 점은 x=10, y=10 이라는 또 다른 ‘상태’를 가질 수 있는 것이 아니다. 단지 x=2, y=3이라는 불변성에 고정되어 있는 것이다. 따라서 수학의 함수 모델을 프로그래밍 세계에 구현한 함수형 프로그래밍에서도 어떠한 상태를 가지거나 상태를 변경해서는 안 되며, 이러한 특징을 곧 불변성
이라고 말한다.