21세기 대부분의 프로그래머들이 처음 배우는 프로그래밍 패러다임은 아마도 객체지향 프로그래밍(OOP)일 것입니다. 객체 지향 프로그래밍은 실제 세계의 개체를 매우 추상적인 모델로 변환하여 기술적 특성과 실행 메서드를 통해 실제 세계에서 그들의 동작을 시뮬레이션할 수 있게 해줍니다. 가독성, 재사용성, 유지보수 가능성 등 수많은 장점에도 불구하고, 상태 관리에 엄청난 복잡성을 도입합니다. 객체 상태를 추적하는 어려움 때문에 지친 프로그래머들은 오류가 발생하기 쉬운 코드를 계속 만들어내고, 발견하고 수정하는 데 몇 시간, 심지어 며칠이 걸릴 버그를 만들어내고 있습니다. "이 바람직하지 않은 상황에서 벗어날 방법이 있을까요?"라고 물을 수도 있습니다. 좋은 소식은 이렇습니다: 덜 알려졌지만 널리 사용되는 프로그래밍 패러다임인 함수형 프로그래밍(Functional Programming, 줄여서 FP)이 상황을 반전시키고 우리가 일으키는 버그 수를 줄일 수 있습니다. 이 글에서는 함수형 프로그래밍이 무엇인지, 그리고 왜 버그를 피하는 데 도움이 될 수 있는지 설명하겠습니다.
함수형 프로그래밍: 순수한 금, 버그 없음
함수형 프로그래밍은 다음과 같은 두 가지 목표를 달성하고자 하는 프로그래밍 패러다임입니다:
- 입력과 출력이 항상 결정론적 관계를 갖도록 보장하기
- 상태 돌연변이와 입출력 스트림 등 부작용을 제거하기
이 결정론적이고 부작용 없는 프로그래밍 접근법의 궁극적인 동기는 코드를 더 쉽게 추론하고 버그를 도입하기 어렵게 만드는 것입니다. 같은 입력이 항상 같은 출력을 내면 불쾌한 놀라움은 절대 일어나지 않을 것입니다. 또한 코드베이스에서 부작용을 제거하면 각 상태가 단계별로 어떻게 변하는지 추적할 필요가 없습니다.
함수형 프로그래밍은 위에서 언급한 목표를 달성하기 위해 순수 함수를 적극적으로 사용합니다. 순수 함수는 다음과 같은 두 가지 특성을 가집니다:
1. 순수 함수는 항상 동일한 인자에 대해 동일한 값을 반환합니다
입력과 출력 간의 이러한 결정론적 매핑은 명백한 장점을 가져옵니다: 순수 함수가 항상 기대한 결과를 반환할 것이라고 믿을 수 있습니다. 이러한 확실성 덕분에 함수가 예상치 못한 결과를 반환해 발생하는 버그를 예방할 수 있습니다.
2. 순수 함수는 부작용을 일으키지 않습니다
부작용 없는 규칙은 각 순수 함수가 그 범위를 벗어난 변화를 일으키지 않아야 한다고 규정합니다. 이 규칙은 비순수 함수가 가하는 외부 상태 변형을 제거하는 데 도움을 주는데, 이는 비순수 함수가 가하는 변형이 덜 눈에 띄고 우리의 주의를 쉽게 피할 수 있어 또 다른 큰 버그 원인이 됩니다.
이제 비순수 함수와 순수 함수의 몇 가지 예를 살펴보겠습니다.
비순수 기능
수치1 = 5
def add(num2: int) -> int:
글로벌 NUM1
Return num1 + num2
# 첫 번째 처형
Print(add(3)) # 8을 반환합니다
숫자 1 = 3
# 두 번째 처형
Print(add(3)) # 6을 반환합니다
위 코드 예시의 '추가' 함수는 동일한 입력에서 동일한 출력을 일관되게 내지 않기 때문에 순수하지 않습니다.
곱 = 0
def multiply_by_two(수: 정수) -> 정체:
글로벌 제품
곱 = 숫자 * 2
반품
# 첫 번째 처형
print(multiply_by_two(3)) # 6을 반환합니다
인쇄물(제품)
# 두 번째 처형
print(multiply_by_two(5)) # 10을 반환합니다
인쇄물(제품)
# 세 번째 처형
print(multiply_by_two(3)) # 6을 반환합니다
인쇄물(제품)
이 코드 예제의 'multiply_by_two' 함수는 동일한 입력을 받으면 동일한 출력을 반환합니다. 하지만 각 호출에서 'product' 변수를 수정하기 때문에 부작용 없음 규칙을 위반하기 때문에 순수 함수는 아닙니다.
순수 기능
방어 add_five(num2: int) -> 지능:
반환 num2 + 5
# 첫 번째 처형
Print(add(3)) # 8을 반환합니다
# 두 번째 처형
Print(add(3)) # 8을 반환합니다
앞서 언급한 '더기' 함수와 달리, 여기서의 'add_five' 함수는 입력과 출력 간의 매핑이 결정론적이기 때문에 순수합니다.
def multiply_by_two(수: 정수) -> 정체:
반환 번호 * 2
# 첫 번째 처형
print(multiply_by_two(3)) # 6을 반환합니다
인쇄물(제품)
# 두 번째 처형
print(multiply_by_two(3)) # 6을 반환합니다
인쇄물(제품)
이 코드 예시에서 'multiply_by_two' 함수는 순수합니다. 항상 동일한 입력을 받으면 동일한 출력을 반환합니다. 또한 부작용도 없습니다.
면책 조항:
한 가지 주목할 점은, 함수형 프로그래밍을 활용하기 위해 목적 지향 프로그래밍(Objective Oriented Programming)과 같은 다른 프로그래밍 패러다임을 포기할 필요는 없다는 것입니다. 함수형 프로그래밍을 다른 패러다임과 원하는 방식으로 결합하셔도 좋습니다!
요약
이 글에서는 함수형 프로그래밍이 무엇인지, 그리고 순수 함수를 어떻게 활용하는지 간략히 소개했습니다. 또한 함수형 프로그래밍에서 순수 함수를 사용하는 것이 무의식적으로 버그를 만드는 것을 방지하는 데 도움이 되는 이유도 설명했습니다. 이 글이 여러분의 함수형 프로그래밍을 시작하는 데 도움이 되었기를 바랍니다.