정수형 변수 x를 선언한다. x에 3이라는 값을 넣는다. x를 아까 정의한 함수 sum()에 전달한다…
C나 Python같은 프로그래밍 언어를 한 번이라도 공부해 본 적 있는 사람들에게 이 문장들은 아주 자연스럽게 이해가 되는 문장들이다. 그러나 기억을 더듬어 보면 처음 이런 개념을 접했을 때는(예를 들어 변수에 값을 할당하는 것) 다소 생소하게 느껴졌을 것이다. 3이면 3이지 그걸 왜 어딘가에 저장을 해야 하는 것이며, 처음에 분명 x는 3이라고 해놓고 왜 어느 순간 보면 값이 바뀌어 있는지.
사실 우리가 보편적으로 알고 있는 프로그래밍 언어는 컴퓨터에게 내릴 명령을 차례로 써내려 가는, 명령형 프로그래밍 언어들이다. 우리는 우리도 모르게 이런 명령형 언어들에 익숙해져 있고, 그 때문에 함수형 프로그래밍을 처음 접하면 대부분 충격을 받는다. 함수형 프로그래밍은 명령형 프로그래밍과는 전혀 다른 사고를 필요로 하기 때문이다. 이 글에서는 이제 막 코딩을 시작하는 사람들과 명령형 언어로만 코딩을 해 봤던 사람들을 위해 함수형 프로그래밍을 짧게 소개하고자 한다.
재밌고 간결한 Functional Programming
시작도 전에 흥미를 잃으면 곤란하므로 어려운 개념은 모두 빼고 흥미로운 특징 몇 가지만 살펴보겠다. 우선 함수형 언어는 값으로 프로그래밍을 하기 때문에 참조 투명성이 보장된다. 어려운 이야기처럼 보이지만 전혀 그렇지 않다. 참조 투명성은 쉽게 말해 동일한 식은 동일한 값만 낸다는 뜻이다. 명령형 언어로 작성된 코드에 x 1이라는 식이 있다고 해보자. 현재 변수 x는 3이라는 값을 저장하고 있다. 그렇다면 저 x 1의 값은 무엇일까? 4라고 대답했다면, 유감스럽게도 틀렸다. 정답은 “모른다”이다. 왜냐하면 변수 x의 값이 언제 어떻게 바뀔지 모르기 때문이다. C로 작성된 다음 코드를 보자.
int x = 3;
x ;
printf(“%dn”, x 1);
화면에는 당연히 5가 출력될 것이다. x의 값이 3에서 4로 바뀌었으므로. 이렇게 변수에 값을 저장하는 것은 어떻게 보면 위험하다. 내가 원한 출력은 4인데, 코드 안의 어딘가에서 변수의 값이 달라진다면 원치 않는 결과가 나올 수 있는 것이다.
반면 함수형 프로그래밍은 x=3을 변수에 값을 넣는 것으로 생각하지 않는다. 함수형 언어에서 x=3이란, x와 3을 완전히 같은 것으로 보는 일종의 정의하는 표현이다. 따라서 앞으로 코드 안에 있는 모든 x는 3으로 치환할 수 있다. 또한 코드 안에 있는 모든 3은 x로 치환할 수 있다. 뿐만 아니라, x 1이라는 식은 항상 4이므로, x 1과 4도 치환할 수 있다. 앞서 한 질문 - x 1의 값은 얼마인가? - 에 이젠 자신 있게 대답할 수 있다. x 1의 값은 항상 4다! 이렇게 식과 값을 항상 치환할 수 있는 것, 그것이 바로 참조 투명성이다.
또 다른 흥미로운 점은, 함수형 프로그래밍에서는 무한대의 데이터를 다룰 수 있다는 것이다. 이게 무슨 말도 안되는 소리인가? 내 컴퓨터의 메모리는 한정되어 있고 절대 무한개의 데이터는 저장할 수 없을텐데. 정확히 말하자면, 데이터를 무한대로 “표현”할 수 있다. 이것이 가능한 이유는 함수형 언어는 지연 연산(lazy evaluation)을 하기 때문인데, 불필요한 계산은 아예 하지 않는 아주 게으른(lazy) 계산 방법이다. 아직 정확하게 이해가 되지 않을 테니 예시 코드를 보자. 다음은 대표적인 함수형 언어인 Haskell로 작성된 코드이다.
sieve (h:t) = h: sieve [ x | x <- t, x ‘mod’ h /= 0]
primes = sieve [2..]
이 코드를 정확히 이해할 필요는 없다. seive는 리스트에 들어있는 데이터들 중에서 자기보다 작은 값으로 나눠지지 않는 데이터만을 걸러주는 역할을 한다. [2..]는 놀랍게도 2부터 시작하는 무한대까지의 정수 리스트를 의미한다. 즉 위의 코드는 2부터 무한대까지의 정수들 중 소수를 걸러내는 코드인 것이다. 만약 내가 50개의 소수가 필요하다면,
take 50 primes
라고 입력하면 된다. 50개의 소수가 담긴 리스트를 얻을 수 있을 것이다. 100개의 소수도, 100000000개의 소수도 원하는 만큼 얻을 수 있다. 코드의 어느 곳에도 구할 수 있는 소수의 범위를 한정 짓는 부분은 없다. 바로 이런 코드가 무한대의 데이터를 표현할 수 있다는 것의 예시다. 내가 몇 개의 소수를 요구하든 간에 이 프로그램은 항상 그만큼의 소수를 돌려준다. 마치 무한대의 소수가 저장되어 있는 것 처럼! 명령형 프로그래밍이었다면 구하고자 하는 소수의 범위를 미리 정해야 할 뿐더러(만약 100 이하의 소수를 구해 놓았는데 사용자가 더 많은 소수를 요구한다면 소스코드 자체를 수정해야 한다..), 반복문을 여러 번 돌려 소수를 걸러냈어야 했을 것이다. 이런 복잡하고 머리 아픈 과정 없이 함수형 코드는 한결 간결하고 직관적임을 알 수 있다.
Functional Programming에 도전하라

[그림] 대표적인 함수형 프로그래밍 언어인 Haskell
여기까지 읽었다면 함수형 프로그래밍에 대한 흥미가 조금은 생겼을 것이다. 생소한 언어인 것은 맞으나 상당히 매력적이고, 이미 많은 사람들이 그 매력에 빠져 함수형 언어를 사용하고 있다. 함수형 언어는 Scheme, Haskell, Erlang 등 많은 종류가 있는데, 각 언어마다 특성이 다르긴 하지만 모두 같은 개념(람다 대수, lambda calculus – 함수형 언어에 흥미가 있다면 꼭 찾아보길 바란다)에서 출발한 언어들이다. 이 언어들로 코드를 작성해 보고, 필자가 처음 느꼈던 그 충격을 느끼는 사람이 많아졌으면 하는 바람이다. 혹시 새로운 언어를 배우는 것이 부담스럽다면 파이썬을 이용해서도 함수형 프로그래밍을 충분히 할 수 있으며 관련 책도 많으니 도전해 보길 바란다.
[저작권자ⓒ CWN(CHANGE WITH NEWS). 무단전재-재배포 금지]