- 함수형 프로그래밍 개념은 프로그래밍 그 자체보다 앞서 등장.
- 함수형 프로그래밍 패러다임의 핵심이 되는 기반은 람다(lambda) 계산법이다. 람다란?
클로저(함수형 언어) VS 자바(객체지향 언어)
- 자바 프로그램은 가변 변수(mutable variable)를 사용하는데, 가변 변수는 프로그램 실행 중에 상태가 변할 수 있다.
- 반복문을 제어하는 변수인 int i = 0 에서 i가 가변 변수이다.
- 클로저 프로그램에서는 이러한 가변 변수가 전혀 없다.
- 클로저에서는 x와 같은 변수가 한 번 초기화되면 절대로 변하지 않는다.
- 함수형 언어에서 변수는 변경되지 않는다!
불변성과 아키텍처
- 아키텍처를 고려할 때 왜 변수의 가변성을 염려해야 하는 것인가?
- 경합(race) 조건, 교착상태(deadlock), 동시 업데이트(concurrent update)문제가 모두 가변 변수로 인해 발생하기 때문.
- 만약 어떠한 변수도 갱신되지 않는다면 경합 조건이나 동시 업데이트 문제가 일어나지 않는다.
- 락이 가변적이지 않다면 교착상태도 일어나지 않는다.
- 그렇다면 불변성이 정말로 실현 가능한것인가?
- 저장 공간이 무한하고 프로세서의 속도가 무한히 빠르다면 가능하겠지만, 현실에서는 제약이 있으므로 타협을 해야 한다.
- 그럼 어떤 타협이 필요한가?
가변성의 분리
이미지 출처 : https://uchanlee.dev/clean-architecture/book/ch6/
- 불변성과 관련하여 가장 주요한 타협 중 하나는 애플리케이션 또는 애플리케이션 내부의 서비스를 가변 컴포넌트와 불변 컴포넌트로 분리하는 일이다.
- 불변 컴포넌트에서는 순수하게 함수형 방식으로만 작업이 처리되며, 어떤 가변 변수도 사용되지 않는다.
- 불변 컴포넌트는 변수의 상태를 변경할 수 있는, 즉 순수한 함수형 컴포넌트가 아닌 하나 이상의 다른 컴포넌트와 서로 통신한다.(그림 6.1)
- 상태 변경은 갖가지 동시성 문제에 노출하는 꼴이므로, 트랜젝션 메모리와 같은 실천법을 사용하여 동시 업데이트와 경합 조건 문제로부터 가변 변수를 보호한다.
- 트랜젝션 메모리는 데이터베이스가 디스크의 레코드를 다루는 방식과 동일한 방식으로 메모리의 변수를 처리한다. 즉, 트랜젝션을 사용하거나 또는 재시도 기법을 통해 이들 변수를 보호한다.
- 현명한 아키텍트라면 가능한 한 많은 처리를 불변 컴포넌트로 옮겨야 하고, 가변 컴포넌트에서는 가능한 한 많은 코드를 빼내야 한다.
이벤트 소싱
- 이벤트 소싱은 상태가 아닌 트랜젝션을 저장하자는 전략이다.
- 상태가 필요해지면 단순히 상태의 시작점부터 모든 트랜젝션을 처리한다.
- 예시
- 자정에 상태를 계산한 후 저장한다.
- 그 후 상태 정보가 필요해지면 자정 이후의 트랜잭션만을 처리하면 된다.
- 데이터 저장소의 저장 공간이 많이 필요할 것이지만, 요즘오프라인 데이터 저장소의 급격한 증가로 충분한 저장 공간을 확보할 수 있다.
- 중요한 점은 데이터 저장소에서 삭제되거나 변경되는 것이 하나도 없다는 사실이다.
- 결과적으로 애플리케이션은 CRUD가 아니라 그저 CR만 수행한다. 또한 데이터 저장소에서 변경과 삭제가 전혀 발생하지 않으므로 동시 업데이트 문제 또한 일어나지 않는다.
- 저장 공간과 처리 능력이 충분하면 애플리케이션이 완전한 불변성을 갖도록 만들 수 있고, 따라서 완전한 함수형으로 만들 수 있다.
- 실제 예로는 Git과 같은 소스 코드 버전 관리 시스템이 정확히 이 방식으로 동작한다.