쏙쏙 들어오는 함수형 코딩 Chapter 1~6
오랜만에 스터디를 시작하였다. 3주동안 하나의 주제, 총 6개의 주제에 대해 다루는 긴 호흡의 스터디인데 중간에 하차하지 않고 끝까지 잘해나가고 싶다!
Chapter 1 : 쏙쏙 들어오는 함수형 코딩에 오신 것을 환영합니다
함수형 프로그래밍 (from 위키)
- 수학 함수를 사용하고 부수 효과를 피하는 것이 특징인 프로그래밍 패러다임
- 부수 효과 없이 순수 함수만 사용하는 프로그래밍 스타일
부수효과
- 함수가 리턴값 이외에 하는 모든일
순수 함수
- 인자에만 의존하고 부수 효과가 없는 함수
But!
실용적인 측면에서 부수 효과는 필요하다, 순수 함수만 쓸 수는 없다.
액션과 계산, 데이터 구분하기
함수형 프로그래머는 코드를 액션, 계산, 데이터로 구분한다.
액션
- 실행 시점이나 횟수에 의존함
계산
- 입력값으로 출력값을 만드는 것
데이터
- 이벤트에 대해 기록한 사실
구분했을 때의 장점
여러 컴퓨터가 네트워크 통신을 하기 시작하면 소프트웨어는 복잡해진다.
이때 실행 시점이나 횟수에 의존하는 코드를 없애면 코드를 더 쉽게 이해할 수 있으며 심각한 버그를 막을 수 있다.
데이터와 계산은 실행 시점이나 횟수에 의존하지 않는다. 그래서 코드를 데이터와 계산로 바꿀수록 소프트웨어에서 생기는 여러 문제를 해결할 수 있다.
액션은 실행 시점이나 횟수에 의존하지만 코드 전체에 영향을 주지 않도록 격리하면 된다.
Chapter 2 : 현실에서의 함수형 사고
2장에서는 주방을 자동화하는 소프트웨어를 예시로 함수형 사고를 적용 해본다.
타임라인 커팅
- 선제 작업이 끝날 때까지 기다린다. (액션이 올바른 순서로 실행할 수 있도록 보장해준다.)
A, B, C, D라는 일이 있고, A, B, C 라는 일이 모두 끝난 후(A, B, C 간의 순서는 없음) D라는 일이 시작되어야 한다면 타임라인 커팅이라는 기술을 활용한다.
Chapter 3 : 액션과 계산, 데이터의 차이를 알기
액션
- 부수 효과가 있는 함수, 순수하지 않은 함수
- 외부 세계에 영향을 주거나 받는 것
- 실행 시점과 횟수에 의존
- 함수로 구현 가능
- 액션을 잘 사용하기 위한 방법
- 가능한 액션을 적게 사용한다
- 액션은 가능한 작게 만든다
- 액션이 호출 시점에 의존하는 것을 제한한다
계산
- 순수함수
- 실행 시점과 횟수에 관계없이 항상 같은 입력값에 대해 같은 출력값을 돌려줌
- 함수로 구현 가능
- 액션보다 좋다 (아래 이유로)
- 테스트가 쉽다
- 분석이 쉽다
- 조합하기 좋다
데이터
- 이벤트에 대해 기록한 사실
- 일어난 일의 결과를 기록한 것
요점 정리
액션 안에는 계산과 데이터, 또 다른 액션이 숨어 있을 수 있다.
액션은 외부 세계에 영향을 주거나 받는다.
계산은 더 작은 계산, 데이터 나눌 수 있다.
계산은 외부 세계에 영향을 주거나 받지 않으며 실행 시점이나 횟수에 의존하지 않는다.
계산은 같은 입력값을 주면 항상 같은 출력값이 나오기 때문에 액션보다 테스트하기 쉽다.
Chapter 4 : 액션에서 계산 빼내기
함수에 암묵적 입력과 출력(부수효과)이 있으면 액션이 된다
계산 추출의 단계
- 계산 코드를 찾아 빼낸다.
- 새 함수에 암묵적 입력과 출력을 찾는다.
- 암묵적 입력은 인자로 암묵적 출력은 리턴값으로 바꾼다.
As-is
function calc_cart_total() {
shopping_cart_total = 0;
for(var i = 0; i < shopping_cart.length; i++) {
var item = shopping_cart[i];
shopping_cart_total += item.price;
}
...
}
To-be
function calc_cart_total() {
shopping_cart_total = calc_total(shopping_cart);
...
}
function calc_total(cart) {
var total = 0;
for(var i = 0; i < cart.length; i++) {
var item = cart[i];
total += item.price;
}
return total;
}
암묵적 입력인 전역변수가 calc_total 안에서 없어졌다. 또한 암묵적 출력을 없애고 함수가 결괏값을 리턴하도록 개선하였다.
Chapter 5 : 더 좋은 액션 만들기
액션은 필요하기 때문에 모든 액션을 없앨 수는 없다. 하지만 암묵적 입력과 출력은 적을수록 좋다
비즈니스 요구 사항과 함수를 맞추기
비즈니스 요구사항이 합계 금액과 제품 가격에 대한 무료 배송 여부가 아니고 주문 결과가 무료 배송인지 확인해야한다면 어떻게 코드를 바꿔야 할까?
As-is
function gets_free_shipping(total, item_price) {
return item_price + total >= 20;
}
function calc_total(cart) {
var total = 0;
for(var i = 0; i < cart.length; i++) {
var item = cart[i];
total += item.price;
}
return total;
}
function update_shipping_icons() {
var buttons = get_buy_buttons_dom();
for(var i = 0; i < buttons.length; i++) {
var button = buttons[i];
var item = button.item;
if(gets_free_shipping(shopping_cart_total, item.price))
button.show_free_shipping_icon();
else
button.hide_free_shipping_icon();
}
}
To-be
function gets_free_shipping(cart) {
return calc_total(cart) >= 20;
}
function calc_total(cart) {
var total = 0;
for(var i = 0; i < cart.length; i++) {
var item = cart[i];
total += item.price;
}
return total;
}
function update_shipping_icons() {
var buttons = get_buy_buttons_dom();
for(var i = 0; i < buttons.length; i++) {
var button = buttons[i];
var item = button.item;
var new_cart = add_item(shopping_cart, item.name, item.price);
if(gets_free_shipping(new_cart))
button.show_free_shipping_icon();
else
button.hide_free_shipping_icon();
}
}
이제 gets_free_shipping 함수는 장바구니가 무료 배송인지 아닌지 알려준다.
암묵적 입력과 출력은 적을수록 좋다
Why?
- 암묵적 입력과 출력이 있다면 다른 컴포넌트와 강하게 연결되었기 때문
설계는 엉켜있는 것을 푸는 것이다
풀려있는 것은 언제든 다시 합칠 수 있다.
- 재사용하기 쉽다.
- 유지보수하기 쉽다.
- 테스트하기 쉽다.
Chapter 6 : 변경 가능한 데이터 구조를 가진 언어에서 불편성 유지하기
불편성 원칙과 copy-on-write를 한다는 것은 같은 의미이다.
copy-on-write란
- 복사본을 만들고 원본 대신 복사본을 변경하는 것
copy-on-write 원칙 세 단계
- 복사본 만들기
- 복사본 변경하기(원하는 만큼)
- 복사본 리턴하기
예시
funcyion add_element_last(array, elem) {
var new_array = array.slice(); // 복사본 만들기
new_array.push(elem); // 복사본 바꾸기
return new_array; // 복사본 리턴하기
}
불변 데이터 구조를 읽은 것은 계산이다.
- 변경 가능한 데이터를 읽는 것은 액션이다.
- 쓰기는 데이터를 변경 가능한 구조로 만든다.
- 어떤 데이터에 쓰기가 없다면 데이터를 변경 불가능한 데이터이다.
- 불변 데이터 구조를 읽은 것은 계산이다.
- 쓰기를 읽기로 바꾸면 코드에 계산이 많아진다.
불변 데이터 구조는 충분히 빠르다.
바뀔 때마다 복사를 하면 너무 비효율적이지않은가? 라고 생각할 수 있다.
하지만 ~
- 언제든 최적화 가능
- 가비지 콜렉터는 매우 빠르다.
- 생각보다 많이 복사하지 않는다.
위와 같은 사례로 일반 애플리케이션에서 사용하기는 충분히 빠르다.