Skip to content

Files

Latest commit

00e371b · Nov 14, 2021

History

History
This branch is 1 commit behind WBBookStudy/CleanArchitectureStudy:main.

06. 함수형 프로그래밍

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Nov 14, 2021

6. 함수형 프로그래밍

정수를 제곱하기

public class Squint {
    public static void main(String args[]) {
        for (int i = 0 ; i < 25 ; i++) {
            System.out.println(i*i);
        }
    }
}

Java의 경우 제곱하기

(println (take 25 (map (fn [x] (* x x)) (range))))

Clojure 언어로 작성 (리스프에서 파생된 언어)

(println ;___ 출력한다.
    (take 25 ;___ 처음부터 25까지의
        (map (fn [x] (* x x)) ;___ 제곱을
            (range)))) ;___ 정수의

위의 기능에 주석을 추가. println, take, range는 모두 함수이다. 리스프에서는 함수를 괄호 안에 넣는 방식으로 호출함.

표현식 (fn [x] (* x x)는 익명함수로, 곱셈 함수를 호출하면서 입력인자를 두 번 전달한다. 즉, 입력의 제곱을 계산한다. 전체코드를 다시볼껀데, 가장 안쪽의 함수 호출부터 시작함..

  • range 함수는 0부터 시작해서 끝이 없는 정수 리스트를 반환한다.
  • 반환된 정수 리스트는 map 함수로 전달되고, 각 정수에 대해 제곱을 계산하는 익명 함수를 호출하여, 모든 정수의 제곱에 대해 끝이 없는 리스트를 생성한다.
  • 제곱된 리스트는 take함수로 전달되고, 이 함수는 앞의 25개까지의 항목으로 구성된 새로운 리스트를 반환한다.
  • println 함수는 입력 값을 출력하는데, 이 경우 입력은 앞의 25개의 정수에 대한 제곱 값으로 구성된 리스트이다.

자바 프로그램은 가변 변수(mutable variable)를 사용하는데, 가변 변수는 프로그램 실행 중에 상태가 변할 수 있다. 앞의 예제에서 반복문을 제어하는 변수인 i가 가변 변수다. 클로저 프로그램에서는 이러한 가변 변수가 전혀 없다. 클로저에서는 x와 같은 변수가 한 번 초기화되면 절대로 변하지 않는다.

불변성과 아키텍처

  • 아키텍처를 고려할 때 위와 같은 가변 변수를 고려해야 하는 이유는 경헙조건, 교착상태조건, 동시 업데이트 문제가 모두 가변변수로 일어나기 때문이다.
  • 아키텍트라면 동시성(concurrency) 문제에 지대한 관심을 가져야만 한다.
  • 불변성이 정말로 실현 가능할까? 자원이 무한대가 아니라면 일종의 타협을 해야만 한다.

가변성의 분리

애플리케이션, 또는 애플리케이션 내부의 서비스를 가변 컴포넌트와 불변 컴포넌트로 분리해야 한다.
불변 컴포넌트에서는 순수하게 함수형 방식으로만 작업이 처리되며, 어떤 가변변수도 사용되지 않는다.
불변 컴포넌트는 변수의 상태를 변경할 수 있는, 즉 순수 함수형 컴포넌트가 아닌 하나 이상의 다른 컴포넌트와 서로 통신한다.
KakaoTalk_Photo_2021-11-14-20-51-25 상태 변경은 컴포넌트를 동시성 문제에 노출하므로 트랜잭션 메모리와 같은 실천법을 사용하여 동시 업데이트와 경합 조건 문제로부터 가변 변수를 보호한다.
-> ex) 클로저의 atom기능

(def counter (atom 0)) ; counter를 0으로 초기화한다.
(swap! counter inc) ; counter를 안전하게 증가시킨다.

이 코드에서 counter변수는 atom으로 정의되었다. 클로저에서 atom은 특수한 형태의 변수로, 값을 변경하려면 반드시 swap! 함수를 사용해야한다. swap! 함수는 2개의 파라미터를 받는다. 하나는 변경할 atom변수, 또 하나는 저장할 새로운 값을 계산할 함수.

atom 기능은 간단한 애플리케이션엔 적합하지만 여러 변수가 상호 의존하는 상황에서는 동시업데이트와 교착상태 문제로부터 완벽히 보호해 주지는 못한다.
말하려는 요지는, 애플리케이션을 제대로 구조화하려면 변수를 변경하는 컴포넌트와 변경하지 않는 컴포넌트를 분리해야한다.

이벤트 소싱

이벤트 소싱은 상태가 아닌 트랜잭션을 저장하자는 전략. 상태가 필요해지면 단순히 상태의 시작점부터 모든 트랜잭션을 처리한다.
(이 전략에 필요한 데이터 저장소에 대해 생각해본다면 현 기술에서 충분하다고 함...)
중요한 점은 데이터 저장소에서 삭제되거나 변경되는 것이 하나도 없다. 결과적으로 애플리케이션은 CRUD가 아니라 그저 CR만 수행한다. 또한 데이터 저장소에서 변경과 삭제가 전혀 발생하지 않으므로 동시 업데이트 문제 또한 일어나지 않는다.
저장 공간과 처리 능력이 충분하면 애플리케이션이 완전한 불변성을 갖도록 만들 수 있다. 즉 완벽한 함수형

구조적 프로그래밍, 객체 지향 프로그래밍, 함수형 프로그래밍

  • 구조적 프로그래밍은 제어흐름의 직접적인 전환에 부과되는 규율이다.
  • 객체지향 프로그래밍은 제어흐름의 간접적인 전환에 부과되는 규율이다.
  • 함수형 프로그래밍은 변수 할당에 부과되는 규율이다.