Book Review

쏙쏙 들어오는 함수형 코딩

luckydipper 2025. 8. 31. 20:34
반응형

본 포스팅은 '쏙쏙 들어오는 함수형 코딩' 책에 대한 서평이다.
기억할 것은 함수형 프로그래밍의 철학, 함수형 프로그래밍 툴 (코드 디자인 방법), 설계방법

1. 함수형 프로그래밍이란?

순수 함수(pure function)를 일급객체(first class object)로 만드는 코드 구조를 갖는다.
프로그램을 데이터, 로직, UI를 분리하는 개념과 비슷하다.

2. 용어 정리

순수 함수, pure funciton

순수 함수는 이상적인 함수다. 책에서는 계산이라고 부르며, 3가지 요건을 만족해야 한다.

  1. Side effect가 없어야 한다.
    내부의 변화가 밖의 시스템에 영향력이 없어야한다. (즉, 전역 변수를 함수 내부에서 호출하는 행위를 하면 안 된다.)
  2. 시간에 관계 없이 같은 동작을 해야 한다.
    이메일 보내기, DB에 저장하기 등등의 작업은 언제 시행되는지, 몇번 실행되는지에 대해 종속적이다.
  3. input이 동일하면 output이 동일해야 한다.
    당연하다.
    이는 마치 전자과에서 배우는 LTI(Linear Time Invariant) system과 비슷하다.

일급 객체, first class object

일급 객체란 함수의 파라미터로 넘겨지는 객체이다. 함수를 일급 객체로 쓰면, 비동기 호출이 끝난후 실행할 함수를 넘겨줄 수 있다. 디버깅 또한 쉬워진다. 이때 넘겨주는 함수의 이름을 handler, callback이라고 한다.
이 함수를 lambda (anonymous function, inline function)로 만들어서 return 값으로 넘길 수 있다.
함수를 input output으로 받는 함수를 high order function이라고 한다. 아래의 함수형 프로그래밍을 도와주는 tool들이 high order function으로 쓰인다.

3. Action, Calculation, Data

함수형 프로그래밍은 코드를 액션 계산 데이터의 3가지 부분으로 나눈다. 코딩 할 때 Data 나 calculation이 많이 작성되도록 코딩한다. Action은 최소화한다. Action을 호출하는 함수도 Action이 되기 때문에 잘 구분해야 한다.

4. Copy on Write vs Defensive Copy

  • Copy on Write: shallow copy로 함수 파라미터로 받아와라. C++에서 foo(const int& param);
  • Defensive Copy: deep copy로 남이 쓴 코드를 호출하기 전에, 파라미터를 copy한다. 호출 후 값을 copy해서 가져온다. 안전지대를 만드는 것이다.
    • API json 파일도 deep copy의 하나이다.
    • 데이터의 불변성을 유지할 수 있도록 해준다.

5. 함수형 프로그래밍을 도와주는 tool

5.1 선형 자료구조의 변형

for_each -> input으로 넣은 array자체가 바뀐다. 데이터의 불변성을 유지하기 어려워서 아래의 3가지를 사용한다.

js c++ content
map transform intput output 크기가 같은 array이다.
filter copy_if if뒤의 절이 맞는 것에 대해 array를 선택한다.
reduce accumulate array의 값을 순회하면서 1가지 값을 추론한다.

reduce를 통해 map과 filter를 구현할 수 있다.

5.2 비선형 자료구조의 변형

json 같은 계층형 key-value값을 조회할 때 필요하다.
"attribute를 문자열, 혹은 enumerate으로 접근가능해야한다."
key-value object를 업데이트 하기 위한 high-order function

function update(object, key, modify){
  var value = object[key];
  var newValue = modify(value);
  var newObject = objectSet(item, field, newValue);
  return newObject;
}

function nestedUpdate(objects, keys, modify){
  if(keys.length === 0)
    return modify(object);
  var key1 = key[0];
  var restOfKey = drop_first(keys);
  return update(object, key1, funciton(value1){
    return nestedUpdate(value1, restOfKeys, modify);
  });
}

동시성 기본형, concurrency primitive

병렬처리의 자원을 공유하기 위한 방법
queue, dropping queue, cell, cut, just once(indepotent)

function DroppingQueue(max, worker){
  var queue_item = [];
  var working = false;

  function runNext(){
    if(working)
      return;
       if(queue_items.length === 0)
      return;
    working = true;
    var item = queue_items.shift();
    worker(item.data, function(val){
      working = false;
      setTimeout(item.callback, 0 ,val);
      runNext();
    }
  }

  return function(data, callback){
      queue_items.push({
      data: data,
      callback: callback || function{}
    })
    while(queue_items.length > max)
      queue_items.shift();
      setTimeout(runNext,0);
   }
}
function calc_cart_worker(cart, done){
  calc_cart_totla(cart,function(total){
      update_total_dom(total);
    done(total);
  }
}
var update_total_queue = DroppingQueue(1,calc_cart_worker);                  

cut, (c++ 에선 latch barrier) 순서 동기화를 위해 기다려야 하는 것.

SW 설계

  • 계층형 설계 (도메인 기반 코드인지. 코드 기반 설계인지. 기능을 위한 설계인지)
    레벨을 나눠서 설계하라. for loop와 함수를 분리하자.
  • 타임 라인을 그리는 방법
    • Action에서 (++, --, += ...)등은 2가지 대입과 증감 연산 두가지 연산을 나뉜다. (읽기 쓰기 구분)
    • parameter로 들어가는 변수는 복사하고 들어간다.

cell: 상태를 공유하는데 사용한다. cell 자체는 변경 가능하지만 변경 불가능한 변수에 담아서 사용한다. watcher(observer,event handler, callback, listener)는 state가 바뀔 때마다 실행되는 handler gkatndlek.

function ValueCell(intialValue){
  var currentValue = initialValue;
  var watcher = [];
  return {
    val: function() {
      return currentValue;
    },
    update: function(f){
      var oldValue = currentValue;
      var newValue = f(oldValue);
      if(oldValue !== newValue){
          currentValue =newValue;
        forEach(watcher,function(watcher){
          watcher(newValue);
        });
      }
    },
    addWatcher: function(f) {
      watcher.push(f);
    }
}
  • 반응형 아키텍처 (이벤트 기반)
  • 순차적 액션의 순서를 바꾼다.
    자료 바뀐 원인 하나당 여러개의 output이 일어나야 할 때 사용한다. (SLAM과 비슷하잖아?)

TODO:

  • concurrent queue에 대한 아종이 많다. blockking queue, immutable queue 등등... 찾아보기
  • effect system: monad applicative functor
반응형