함수형 프로그래밍 개요
함수형 프로그래밍은 사례로 설명하는 것이 가장 좋다.
자바에서 0부터 9까지의 수의 제곱을 출력하려면 이렇게 한다.
for(int i=0; i<10; i++){
System.out.println(i*i);
}
Java
복사
함수형 언어인 클로저를 이용하면 이렇게 한다.
(println (take 10 (map (fn [x] (* x x)) (range)))
Clojure
복사
•
range 함수는 0 부터 시작해서 끝이 없는 정수 리스트를 반환한다.
•
map 함수는 range 함수가 반환한 리스트를 제곱을 반환하는 익명함수를 이용해 끝이 없는 정수 제곱 리스트를 생성한다.
•
제곱된 리스트는 take 함수로 전달되고, 10 개만 선택되어 println 함수에 전달된다.
눈여겨 봐야 할 것은 자바에서는 i 가 매번 변했지만, 클로저에서는 어떤 순간에도 변수의 값이 변경되지 않았다는 점이다. 즉, 함수형 언어에서 변수는 변경되지 않는다.
불변성과 아키텍처
변하지 않는다면 경합 조건, 교착상태 조건, 동시 업데이트 등이 발생할 수 없다. 당연한 말이지만, 어떤 변수도 갱신되지 않는다면 경합 조건이나 동시 업데이트가 발생할 수 없고, 락이 가변적이지 않으면 교착상태 역시 발생할 수 없다.
다수의 스레드와 프로세스로 인해 발생하는 문제는 가변 변수가 없으면 생기지 않는다.
물론, 현실적으로 가변 변수 없이 시스템을 만드는 것은 불가능하기 때문에 타협해야 한다.
가변 컴포넌트와 불변 컴포넌트의 분리
불변성과 관련한 가장 큰 타협은 가변 컴포넌트와 불변 컴포넌트를 분리하는 것. 불변 컴포넌트는 가변 변수가 하나도 없는 멐포넌트. 불변 컴포넌트는 하나 이상의 가변 컴포넌트와 통신한다. 가변 컴포넌트에서는 동시성 문제를 해결하기 위해 트랜잭션 메모리, 비교 및 교체 알고리즘 등을 이용해 가변 변수들을 적절히 보호해야 한다.
아키텍트는 가능한 많은 처리를 불변 컴포넌트로 옮기고, 가변 컴포넌트에서는 가능한 많은 처리를 빼야 한다.
이벤트 소싱
이벤트 소싱은 상태를 저장하는 것이 아니라, 트랜잭션을 저장한다. 특정 순간의 상태를 원하면, 그 시점까지의 트랜잭션을 실행하여 알아낸다. 저장공간은 기존보다 많이 필요하겠지만 이제는 그 공간이 가능하다.
당연히 트랜잭션은 의미상으로 이미 실행된 어떤 동작이기 때문에 수정이나 삭제라는 개념 자체가 불가능하다. 따라서 이벤트 소싱에서는 CRUD 가 아니라 CR만 가능하다. 즉, 수많은 동시성 문제가 해결된다. 저장 공간과 처리 능력이 충분하면 애플리케이션이 완전한 불변성을 갖도록 만들 수 있다. 소스코드 버전 관리 시스템이 실제로 이런 식으로 동작한다. 모든 변경사항 기록을 저장한다!
3 ~ 6장 요약
•
구조적 프로그래밍은 제어흐름의 직접적인 전환에 부과되는 규율이다.
•
객체 지향 프로그래밍은 제어흐름의 간접적인 전환에 부과되는 규율이다.
•
함수형 프로그래밍은 변수 할당에 부과되는 규율이다.
이 패러다임들은 프로그래머로부터 무언가를 뺏어갔다. 프로그래머들이 지난 50여년동안 배운 것은 해서는 안되는 것이다.
도구도 언어도 변했지만, 소프트웨어는 순차, 분기, 반복, 참조 로 구성된다는 것은 튜링 시절부터 지금까지 변하지 않았다.