FP Workshop - 1. Functional vs Imperative


Chapter 1: Functional vs Imperative

명령형 스타일로 된 코드를 함수형 스타일로 바꾸어 보기

js

const products = [
  { id: "candy", name: "Candy", price: 10, onSale: true },
  { id: "ice-cream", name: "Ice cream", price: 20, onSale: true },
  { id: "cake", name: "Cake", price: 30, onSale: false },
  { id: "donuts", name: "Donuts", price: 12, onSale: true },
  { id: "chocolate", name: "Chocolate", price: 15, onSale: false },
  { id: "flower", name: "Flower", price: 40, onSale: false },
  { id: "sofa", name: "Sofa", price: 120, onSale: true },
  { id: "bed", name: "Bed", price: 400, onSale: true },
];

1. Imperative

가격이 30 이하인 제품 중에서 3개를 뽑고 그 가격들의 총합을 구하기.

js

// Imperative
function imperative() {
  let total = 0;
  let count = 0;
  for (const a of products) {
    if (a.price < 30) {
      total += a.price;
      count++;
    }
    if (count === 3) break;
  }
  console.log(total);
}

2. Functional

js

const isIterable = (iter) => typeof iter[Symbol.iterator] === "function";
const curry =
  (f) =>
  (a, ...args) =>
    args.length ? f(a, ...args) : (...args2) => f(a, ...args2);

const map = curry(function* (f, iter) {
  for (const a of iter) {
    yield f(a);
  }
});

const filter = curry(function* (f, iter) {
  for (const a of iter) {
    if (f(a)) yield a;
  }
});

const take = curry((length, iter) => {
  const res = [];
  for (const a of iter) {
    res.push(a);
    if (res.length === length) return res;
  }
});

const reduce = curry((f, acc, iter) => {
  if (!iter && isIterable(acc)) {
    iter = acc[Symbol.iterator]();
    acc = iter.next().value;
  }

  for (const a of iter) {
    acc = f(acc, a);
  }

  return acc;
});

const add = curry((a, b) => a + b);

const pipe =
  (...args) =>
    reduce((a, f) => f(a), args);

// Functional
pipe(
  products,
  map((a) => a.price),
  filter((p) => p < 30),
  take(3),
  reduce(add),
  console.log,
);

3. Generator 와 Lazy Evaluation(지연평가)

js

pipe(
  products,
  map((a) => a.price), // Lazy
  filter((p) => p < 30), // Lazy
  take(3),
  reduce(add),
  console.log,
);

해당 코드에서 map, filter는 Generator 를 사용하였기 때문에 take가 3개를 만족하게 되면 더이상 실행되지 않는다.

4. 함수형의 장점

  • 가독성이 좋음
  • 재사용성이 좋음
  • 수정이 용이함