← 블로그 목록

함수형 프로그래밍에서 재귀가 중요한 이유는 루프를 금지해서가 아니라 상태를 드러내기 위해서다

함수형 프로그래밍에서 재귀는 루프의 대체재라기보다 상태 변화를 인자로 드러내는 방식에 가깝다. 특히 꼬리 재귀와 누산기 패턴을 이해하면 이 차이가 분명해진다.

함수형 프로그래밍에서 재귀가 중요한 이유는 루프를 금지해서가 아니라 상태를 드러내기 위해서다

함수형 프로그래밍에서 재귀가 중요한 이유는 루프를 금지해서가 아니라 상태를 드러내기 위해서다

함수형 프로그래밍을 처음 볼 때 가장 낯선 장면 가운데 하나가 재귀다. 명령형 코드에서는 forwhile로 자연스럽게 풀던 반복을, 함수형 코드에서는 자기 자신을 다시 호출하는 방식으로 표현하는 경우가 많기 때문이다.

하지만 핵심은 “루프를 없애는 마술”이 아니다. 더 중요한 차이는 반복 과정에서 무엇이 바뀌는지를 더 분명하게 드러낸다는 점에 있다.


재귀는 숨은 상태를 인자로 꺼내 놓는다

명령형 루프에서는 보통 두 가지가 숨어 있다.

재귀로 바꾸면 이 둘이 함수 인자로 밖으로 드러난다. 그래서 함수가 “지금 어디까지 왔는가”, “무엇을 쌓아 가고 있는가”를 더 직접적으로 보여 준다.

이 점은 특히 계산 규칙을 테스트하거나, 상태 전이를 추적해야 하는 코드에서 장점이 있다. 재귀가 멋져 보여서가 아니라, 반복에 숨어 있는 상태를 드러내기 때문이다.


꼬리 재귀는 재귀를 실용적으로 만드는 핵심이다

에를랑(Erlang) 공식 문서는 함수 본문의 마지막 표현식이 함수 호출이면 tail recursive call, 즉 꼬리 재귀 호출이 이뤄진다고 설명한다. 그리고 이런 꼬리 재귀 호출을 이용한 무한 루프는 호출 스택을 소진하지 않고 원칙적으로 계속 실행될 수 있다고 적고 있다.

이 차이는 실무적으로 크다.

그래서 함수형 코드에서 재귀를 쓸 때 중요한 것은 “재귀를 쓰느냐”보다 꼬리 재귀 형태로 정리할 수 있느냐에 더 가깝다.


누산기 패턴은 함수형 반복의 가장 실용적인 형태다

꼬리 재귀를 이해할 때 자주 만나는 형태가 누산기(accumulator) 패턴이다. 중간 결과를 함수 바깥 변수에 저장하는 대신, 다음 호출의 인자로 넘기는 방식이다.

이 방식이 좋은 이유는 단순하다.

즉 누산기 패턴은 함수형 프로그래밍의 미학이라기보다, 반복을 더 예측 가능하게 만드는 도구다.


순수 함수와 재귀는 자주 같이 나오지만 같은 개념은 아니다

순수 함수는 같은 입력에 대해 같은 출력을 돌려주고, 바깥 상태에 부작용을 만들지 않는 함수를 뜻한다. 재귀는 그런 순수 함수를 작성할 때 자주 쓰이는 표현 방식일 뿐, 둘은 같은 말이 아니다.

재귀 함수도 외부 상태를 읽거나 쓰면 순수하지 않을 수 있고, 반대로 반복문을 쓴다고 해서 반드시 비순수한 것도 아니다. 그래서 함수형 프로그래밍을 공부할 때는 “재귀 = 순수함”처럼 묶어 이해하기보다, 재귀는 반복을 표현하는 방식, 순수 함수는 부작용을 다루는 규칙으로 따로 보는 편이 좋다.


핵심 정리

함수형 프로그래밍에서 재귀가 중요한 이유는 루프를 금지해서가 아니다. 반복 과정에 숨어 있던 상태를 인자로 꺼내 놓고, 그 흐름을 더 분명하게 보여 주기 때문이다.

특히 에를랑 문서가 설명하는 꼬리 재귀와 누산기 패턴을 이해하면, 재귀는 “신기한 문법”이 아니라 반복을 더 명시적으로 다루는 실용적인 방법으로 보이기 시작한다. 함수형 프로그래밍의 핵심은 결국 마술이 아니라 상태를 어떻게 드러내고 다룰지에 있다.

참고 자료

← 목록으로
Related

함께 읽으면 좋은 글

함수형 프로그래밍불변성자료구조
불변 자료구조가 비효율적으로 보이는데도 계속 쓰이는 이유

불변 자료구조는 매번 전체를 복사하는 비효율적인 방식처럼 보이지만, 실제 구현은 구조적 공유와 영속 자료구조를 바탕으로 훨씬 더 실용적으로 동작한다.

함수형 프로그래밍게임 개발불변성
함수형 프로그래밍이 게임 개발에서 유용한 곳은 상태를 없애는 곳이 아니라 상태 변화를 분리하는 곳이다

함수형 프로그래밍은 게임 코드를 전부 다시 쓰라는 명령이 아니다. 불변 데이터와 순수 함수의 관점을 이용해 규칙 계산, 상태 전이, 서버 메시지 처리처럼 변화를 분리해야 하는 영역을 더 다루기 쉽게 만든다.

객체지향상속델리게이트
상속을 줄이고 델리게이트와 시그널로 푸는 이유

상속은 ‘무엇의 하위 타입인가’를 표현하는 데 강하지만, UI 이벤트나 객체 간 통신처럼 호출 관계가 자주 바뀌는 영역에서는 델리게이트나 시그널과 슬롯 같은 느슨한 연결이 더 잘 맞는다. C# 델리게이트와 Qt 시그널의 사례를 빌려, 좋은 설계는 상속을 줄이는 신념이 아니라 ‘타입 관계’와 ‘실행 시 연결 관계’를 분리해 보는 습관에서 나온다는 점을 정리한다.