콘텐츠로 바로가기

함수형 프로그래밍 패러다임 (Functional Programming Paradigms)

함수형 프로그래밍 패러다임이 상태 변화보다 값 변환과 합성을 중심에 두는 방식을 정리한 학습 노드입니다.

Article
M

Me

hyunyoun's Blog

programming-languages-compilersprogramming-languagescompilerslanguage-theorytype-systemsfunctional-programming-paradigmslanguages-compilerslearning9 min read

1. Overview

함수형 프로그래밍 패러다임(Functional Programming Paradigms)부작용(Side Effect) 없는 순수 함수(Pure Function), 불변 데이터(Immutable Data), 함수를 일급 시민(First-Class Citizen)으로 취급하는 프로그래밍 패러다임으로, 코드의 예측 가능성과 테스트 가능성을 높입니다.

학습자는 람다 대수(Lambda Calculus)의 핵심인 λx.x+1 표현식이 현대 언어의 람다/클로저(Closure)와 어떻게 연결되는지 살펴봅니다. 나아가 map, filter, fold/reduce 같은 고차 함수(Higher-Order Function)의 구성 가능성(Composability), 커링(Currying)과 부분 적용(Partial Application)의 함수 팩토리 패턴, **모나드(Monad)**가 순수 함수형 세계에서 I/O와 부작용을 다루는 방식, 그리고 순수 함수형 데이터 구조의 영구불변성(Persistence)까지 분석하여 Haskell, Scala, Elixir, 현대 JavaScript/Python의 함수형 스타일을 이해하는 역량을 확보합니다.

2. Scope & Boundaries

In-Scope

  • 람다 대수 기초 (Lambda Calculus): α-변환, β-환원, η-변환, 자유 변수/묶인 변수, 커링(Currying).
  • 고차 함수 (Higher-Order Functions): map, filter, reduce/fold, 함수 합성(compose, .), 부분 적용(Partial Application), functools.partial.
  • 클로저와 커링 (Closure & Currying): 클로저의 환경 캡처, 커링된 함수 팩토리, 데이터 캡슐화.
  • 모나드 (Monad): Maybe/Option 모나드, IO 모나드, flatMap/>>=, 모나드 법칙.
  • 불변 데이터 구조 (Persistent/Immutable Data): 구조 공유(Structural Sharing), Persistent List/Tree.

Out-of-Scope

  • Haskell 전체 언어 스펙: Typeclass, Functor, Applicative의 범주론(Category Theory) 심화 → 전문 FP 이론.
  • 리액티브 프로그래밍(RxJS/Reactor): 비동기 스트림 합성 → 05-01-04 Concurrency Models 영역.

Boundaries

  • FP vs OOP 트레이드오프: FP는 데이터와 함수를 분리하여 함수 합성으로 복잡도를 제어합니다. OOP는 데이터와 동작을 캡슐화하여 은닉(Encapsulation)으로 복잡도를 제어합니다. 부작용(네트워크, DB, UI)이 많은 대규모 시스템은 OOP의 캡슐화가, 데이터 변환 파이프라인(ETL, 데이터 처리)은 FP의 함수 합성이 더 자연스럽습니다.

3. Counterexample

  • 부작용 있는 "순수 함수"의 숨은 상태 참조 (Hidden Side Effect): "이 함수는 순수해(Pure)요. 입력만 사용하고 출력만 반환해요"라는 주장의 코드 def get_total(items): global tax_rate; return sum(i.price for i in items) * tax_rate. tax_rate라는 전역 변수를 참조하므로, 다른 코드가 tax_rate를 변경하면 같은 items 입력에 다른 결과를 냅니다. 이는 순수 함수(Pure Function, 같은 입력 → 항상 같은 출력, 전역 상태 참조 없음)의 정의를 위반하며, 단위 테스트가 실행 순서에 따라 통과/실패하는 불확실성을 만듭니다.
  • Fold/Reduce의 결합성 오류 (Fold Associativity Mistake): fold_right(λa b → a - b, 0, [1,2,3,4]) vs fold_left(λa b → a - b, 0, [1,2,3,4]). Fold Right: 1-(2-(3-(4-0))) = 1-2+3-4 = -2. Fold Left: ((((0-1)-2)-3)-4) = -10. 뺄셈(비결합적 연산)에서 fold의 방향(Left vs Right)이 결과를 완전히 바꿉니다.

4. Prerequisites

  • 함수 개념 (Basic): 함수가 값을 받아 값을 반환한다는 수학적 정의를 이해해야 합니다.
  • 재귀 (Basic): FP의 반복 대안이 재귀이므로 재귀 사고 필수. (04-03-01 Recursion)

5. Learning Map

Sequence Core Cluster Objective & Description Evidence (BoK)
1 Pure Functions & Lambda Calculus 부작용 없는 순수 함수의 수학적 정의와, 람다 대수의 β-환원으로 함수 적용을 계산하는 방식을 이해합니다. P1
2 Higher-Order Functions & Composition map/filter/reduce로 데이터 변환 파이프라인을 구성하고, 함수 합성(compose)으로 복잡도를 제어하는 방식을 익힙니다. P5
3 Currying & Closures 함수가 환경을 캡처하는 클로저의 동작과, 커링으로 다중 인자 함수를 단일 인자 함수 체인으로 변환하는 패턴을 살펴봅니다. Industry
4 Monads & Immutable Data Maybe 모나드로 null 처리를 체인하고, 구조 공유 영구불변 데이터 구조의 O(1) 불변 업데이트를 이해합니다. Industry

6. Learning Topics

Basic

Core Topic 01: 부작용 없는 수학 함수와 람다의 기원, 순수 함수와 람다 대수 (Pure Functions & Lambda Calculus)

  • Why to Learn: 함수형 프로그래밍의 "순수성(Purity)"이 단순한 스타일 선택이 아니라 "함수 = 수학적 함수(동일 입력 → 동일 출력, 부작용 없음)"라는 계약임을 이해하고, 이 계약이 병렬 처리, 테스트, 캐싱을 어떻게 안전하게 만드는지 파악하기 위해서입니다.
  • What to Learn:
    • Concepts: 순수 함수(Pure Function), 참조 투명성(Referential Transparency), 람다 대수(λx.e), β-환원(Beta Reduction), 자유 변수/묶인 변수.
    • Skills: 사이드 이펙트(전역 변수 참조, I/O, 상태 변경) 식별, 순수/비순수 함수 구분.
  • How to Learn:
    • 1단계: f(x) = x + 1 순수 함수. f(5)=6, 항상. 반면 g(x) = x + counter (전역 counter 참조)는 counter가 변하면 g(5)가 달라지는 비순수 함수. 순수 함수만으로 구성된 표현식은 어디서든 해당 값으로 대체 가능(참조 투명성)함을 확인합니다.
    • 2단계: 람다 대수 β-환원: (λx. x+1) 5 = [x←5] x+1 = 5+1 = 6. 고차 함수: (λf. λx. f(f(x))) (λx. x+1) 3 = (λx. x+1)((λx. x+1) 3) = (λx. x+1)(4) = 5. 함수를 인자로 전달하는 FP의 핵심을 살펴봅니다.
  • Implement: 파이썬 순수 함수 스타일 리팩토링. 전역 상태 참조 함수 add_tax(price) → 세율을 인자로 받는 add_tax(price, rate) 순수 변환. functools.lru_cache로 순수 함수를 자동 메모이제이션(같은 입력 → 항상 같은 출력이라 캐싱 안전)하는 성능 최적화 데모.

Core Topic 02: 데이터 파이프라인의 레고 블록, 고차 함수와 합성 (Higher-Order Functions)

  • Why to Learn: Pandas의 df.groupby().agg(), Java Stream의 .filter().map().collect(), JavaScript Array의 .filter().map().reduce() 모두 고차 함수 합성 패턴이며, 이 패턴으로 복잡한 데이터 변환을 선언적으로(How가 아닌 What) 표현하는 역량을 갖추기 위해서입니다.
  • What to Learn:
    • Concepts: 고차 함수(Higher-Order Function, 함수를 인자/반환값으로 받는 함수), map, filter, reduce/fold, 함수 합성(Function Composition, (f∘g)(x) = f(g(x))).
    • Skills: 파이프라인 구성(data | filter | map | reduce), functools.reduce, Python 리스트 컴프리헨션 vs 함수형 스타일 비교.
  • How to Learn:
    • 1단계: 주문 목록에서 할인가 계산: orders.filter(is_valid).map(calculate_price).reduce(sum). 각 함수가 하나의 책임만 가지는 FP 파이프라인의 선언적 스타일을 분석합니다.
    • 2단계: 함수 합성 compose(f, g)(x) = f(g(x)). double = lambda x: x*2, add_one = lambda x: x+1, double_then_add_one = compose(add_one, double). double_then_add_one(5) = 11. 합성으로 복잡한 변환을 조각 함수 조합으로 표현하는 방식을 살펴봅니다.
  • Implement: 파이썬 compose(*fns) 유틸리티. compose(f, g, h)(x) = f(g(h(x))). 주문 데이터 파이프라인: process = compose(calculate_total, apply_discount, filter_valid). [{price:100, valid:True}, {price:200, valid:False}](./{price:100,-valid:true},-{price:200,-valid:false}) 처리 후 결과 검증. 명령형 루프 버전 vs FP 파이프라인 버전 코드 줄 수/가독성 비교.

Practical

Core Topic 03: 함수가 환경을 삼키다, 클로저와 커링 (Closures & Currying)

  • Why to Learn: JavaScript의 즉각 실행 함수(IIFE), React의 useState 훅, Python의 데코레이터, Redux의 미들웨어가 모두 클로저와 커링 패턴의 응용임을 이해하기 위해서입니다.
  • What to Learn:
    • Concepts: 클로저(Closure, 자유 변수를 캡처하는 함수), 환경(Environment = 변수 바인딩 테이블), 커링(Currying, f(x,y) → f(x)(y)), 부분 적용(Partial Application, 인자 일부만 고정).
    • Skills: functools.partial, 커링된 함수 팩토리, 클로저로 private 상태 캡슐화.
  • How to Learn:
    • 1단계: 클로저: def make_adder(n): return lambda x: x + n. add5 = make_adder(5). add5(3)8. n=5라는 외부 변수를 람다가 캡처(Capture)하여 make_adder가 이미 종료된 후에도 n에 접근 가능한 클로저 동작을 분석합니다.
    • 2단계: 커링: add(x, y) → add_curried(x)(y). add_curried(1)(2) = 3. add_curried(1) → 새 함수 lambda y: 1+y 반환(부분 적용). double = add_curried(0) + ... 대신 multiply = lambda base: (lambda x: base * x) 팩토리 패턴을 살펴봅니다.
  • Implement: 파이썬 curry(f) 함수. @curry 데코레이터로 def add(x, y, z): return x+y+zadd(1)(2)(3)add(1, 2)(3) 모두 작동. make_counter() 클로저: 호출마다 count 증가하는 private 상태 캡슐화. counter = make_counter(); counter(); counter(); counter()[1,2,3] 출력.

Advanced

Core Topic 04: 부작용을 감싸는 수학적 컨테이너, 모나드와 불변 데이터 (Monad & Immutable Data)

  • Why to Learn: 순수 함수형 언어(Haskell)에서 I/O, null 처리, 오류 전파를 순수성을 깨지 않고 처리하는 모나드의 철학과, 함수형 데이터 구조가 기존 데이터를 복사 없이 O(log n)에 "업데이트"하는 구조 공유 방식을 이해하기 위해서입니다.
  • What to Learn:
    • Concepts: 모나드(Monad, return>>= 연산), Maybe 모나드(None 전파), IO 모나드(순수 코드와 I/O 분리), 모나드 법칙(Monad Laws).
    • Skills: flatMap 체인으로 null 체크 없이 안전한 연산 체인, 영구불변 리스트/트리 구조 공유.
  • How to Learn:
    • 1단계: Maybe 모나드: safe_divide(10, 0)Nothing. safe_log(-1)Nothing. 체인: safe_divide(10, 2).flatMap(safe_log).flatMap(round). 중간에 Nothing이 발생하면 전체가 Nothing으로 단락(Short-circuit). null 체크 없는 안전한 체인 방식을 분석합니다.
    • 2단계: 영구불변 리스트 구조 공유: list1 = [1,2,3,4]cons(0, list1)list2 = [0,1,2,3,4]. 내부적으로 list2의 tail이 list1을 공유(동일 메모리 포인터)하여 복사 없이 O(1) cons 연산이 가능한 구조 공유 방식을 살펴봅니다.
  • Implement: 파이썬 Maybe 모나드 클래스. class Just(Generic[T]), class Nothing. bind(f) -> Maybe[S]: Just면 f 적용, Nothing이면 Nothing 반환. Just(10).bind(lambda x: Just(x/2)).bind(lambda x: Nothing() if x<0 else Just(x)).bind(str) 체인 출력. 영구불변 링크드 리스트 PList.cons(val, tail) 구조 공유 증명(is 연산자로 동일 메모리 확인).

7. Terminology

Term (EN / ko, abbr) 1문장 정의 단계(기본/권장/실무/심화) 역할/맥락 관련 개념 유사/대비/함께 사용 오해 포인트 Evidence(Primary/Secondary/Industry) Flags(core)
Functional Programming 변수 할당(상태 변경)을 금지하고, 입력이 같으면 항상 같은 출력을 반환하는 순수 함수(Pure Function)들의 합성만으로 프로그램을 구축하는 패러다임입니다. 기본 언어 패러다임 Declarative Programming Imperative/OOP 루프(for) 대신 재귀나 고차 함수(map/reduce)를 강제함 P1:CS2023 core
Pure Function 외부에 있는 상태를 읽거나 수정하지 않고(Side-effect Zero), 오직 입력 매개변수에만 의존하여 결과를 반환하는 함수입니다. 권장 버그 억제 Referential Transparency Impure Function DB 접근이나 콘솔 출력(I/O)도 순수 함수 세계에선 부작용임 P5:SFIA core
Higher-Order Function 함수 자체를 매개변수(인자)로 전달받거나, 함수를 결과값으로 반환할 수 있는 일급 객체(First-class Citizen) 성질을 활용한 함수입니다. 실무 코드 추상화 Map/Filter/Reduce Callback Function 단순 콜백이 아니라 함수를 조립(Composition)하는 구조로 쓰임 Industry core
Monad 부수 효과(I/O, 에러, 상태)를 포함하는 연산을 안전한 캡슐(컨텍스트)로 감싸, 순수 함수의 체이닝(Chaining)을 유지시키는 디자인 패턴입니다. 심화 제어 흐름 제어 Functor / Promise Null Pointer Exception 방어 억지로 모나드를 남용하면 코드가 오히려 난해해짐 Industry core

8. References

Primary

  • [P1] CS2023 - Programming Languages (PL) - Functional Programming
  • [P5] SFIA - Software Development (PROG) - Declarative Paradigms

Secondary

  • [Structure and Interpretation of Computer Programs (SICP)] Abelson & Sussman - Functional Abstraction
  • [Category Theory for Programmers] Bartosz Milewski - Monads and Functors

Industry

  • [Haskell Wiki] - Pure functions, Monads, and Lazy Evaluation
  • [React Documentation] - Functional Components and Hooks (Immutability)

9. Final Checklist

Primary

  • 상태 변경(Mutation)과 부수 효과(Side-effect)가 멀티스레드 환경에서 유발하는 데이터 레이스(Data Race)를 순수 함수가 어떻게 줄이는지 설명할 수 있는가?
  • map, filter, reduce와 같은 고차 함수(Higher-Order Function)를 사용하여 절차지향적 for 루프를 선언적 데이터 파이프라인으로 리팩터링할 수 있는가?

Secondary

  • 참조 투명성(Referential Transparency)이 컴파일러의 메모이제이션(Memoization) 캐싱 최적화에 미치는 영향을 설명할 수 있는가?
  • 불변 데이터 구조(Immutable Data Structures)가 값을 변경할 때 딥 카피(Deep Copy) 대신 구조적 공유(Structural Sharing)를 사용하는 방식을 설명할 수 있는가?

Industry

  • 프론트엔드 React나 백엔드 비동기 처리에서 모나드(Monad) 철학(예: Promise, Optional, Result)이 예외 처리를 파이프라인 체이닝으로 어떻게 다루는지 논증할 수 있는가?
  • 지연 평가(Lazy Evaluation)를 통해 무한 리스트(Infinite Stream)를 다룰 때 발생하는 메모리 지연 할당의 이점과, 반대로 썽크(Thunk) 누적으로 인한 메모리 릭 위험을 설계 관점에서 저울질할 수 있는가?

Languages Compilers · Language Theory & Type Systems

4 / 5