Skip to content

Latest commit

 

History

History
615 lines (533 loc) · 25.7 KB

README_kr.md

File metadata and controls

615 lines (533 loc) · 25.7 KB

EN | KR

FxJS - Functional Extensions for Javascript

npm npm bundle size npm NPM

FxJS는 ECMAScript 6 기반의 함수형 프로그래밍 라이브러리입니다. Iterable, Iterator, Generator, Promise를 다룹니다.

Getting Started

Installation

In Modern Browsers Supporting ES6

fx.js는 ECMAScript Module로 작성된 FxJS를 브라우저에서 실행할 수 있는 하나의 스크립트 파일로 번들링한 것입니다.

주의: fx.js는 window 객체의 fx, _, L, C property를 namespace로 사용합니다.

<script src="https://unpkg.com/fxjs/dist/fx.min.js"></script>
const { map, filter, reduce, L, C } = _;

map((a) => a + 1, [1, 2, 3]);
// [2, 3, 4];

In Legacy ES5 Browsers

fx.es5.jsIE11 브라우저를 타겟으로 FxJS를 빌드한 스크립트 파일입니다.

주의: fx.js와 마찬가지로, fx.es5.js역시 window 객체의 fx, _, L, C property를 namespace로 사용합니다.

<script src="https://unpkg.com/fxjs/dist/fx.es5.min.js"></script>
_.reduce(
  (a, b) => a + b,
  L.map((a) => a + 1, [1, 2, 3])
);
// 9;

In Node.js

FxJS는 ECMAScript Module으로 개발되고 있습니다. 하지만 fxjs패키지에 배포하는 파일들은 Node.js 6버전을 target으로 변환된 CommonJS Module입니다.

npm install fxjs
const FxJS = require("fxjs");
const _ = require("fxjs/Strict");
const L = require("fxjs/Lazy");
const C = require("fxjs/Concurrency");

// fxjs의 default export module 객체는 Lazy, Concurrency를 포함한 모든 함수를 가지고 있습니다.
const { reduce, filterL, mapC } = FxJS;

// 함수를 개별적으로 가져올 수도 있습니다.
const rangeL = require("fxjs/Lazy/rangeL");

_.go(
  rangeL(1, 5),
  filterL((a) => a % 2),
  L.map((a) => a * 10),
  reduce(_.add),
  _.log
); // 40

CommonJS 모듈은 기본적으로 번들러가 Tree-Shaking을 지원되지 않기 때문에 fxjs package를 사용할 때, 개별 함수를 불러오는 방식으로 사용하는 것을 권장합니다.

import rangeL from "fxjs/Lazy/rangeL";
import filterL from "fxjs/Lazy/filterL";
import mapL from "fxjs/Lazy/mapL";
import go from "fxjs/Strict/go";
import add from "fxjs/Strict/add";
import reduce from "fxjs/Strict/reduce";
import log from "fxjs/Strict/log";

go(
  rangeL(1, 5),
  filterL((a) => a % 2),
  mapL((a) => a * 10),
  reduce(add),
  log
); // 40

ECMAScript Module

FxJS는 Native ECMAScript Module로만 작성된 fxjs2패키지를 별도로 배포하고 있습니다. fxjs2패키지는 package.json파일에 type: "module"로 정의되어 있습니다. mocha와 jest같은 개발 도구에서 아직 Native ESM을 잘 지원하지 않기 때문에 주의해야 합니다.

npm install fxjs2
import { go, reduce, add, log } from "fxjs2";
import * as L from "fxjs2/Lazy/index.js";

go(
  L.range(1, 5),
  L.filter((a) => a % 2),
  L.map((a) => a * 10),
  reduce(add),
  log
); // 40

Iteration protocols

제너레이터를 통해 만든 이터레이터를 FxJS의 함수들로 평가할 수 있습니다.

function* fibonacci() {
  let a = 0,
    b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const f = pipe(
  fibonacci,
  L.filter((n) => n % 2 == 0),
  L.takeWhile((n) => n < 10)
);

const iterator = f();
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 8, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

reduce((a, b) => a + b, f());
// 10

Iterable programming

어떤 값이든 [Symbol.iterator]() 메서드를 가진 이터러블이라면 FxJS와 사용할 수 있습니다.

const res = go(
  [1, 2, 3, 4, 5],
  filter((a) => a % 2),
  reduce(add)
);

log(res); // 9

Lazy evaluation

L 네임스페이스의 함수를 통해 지연 평가를 할 수 있습니다.

const res = go(
  L.range(Infinity),
  L.filter((a) => a % 2),
  L.take(3),
  reduce(add)
);

log(res); // 9

FRP style

Functional reactive programming 스타일을 작성할 수 있습니다.

go(
  L.range(Infinity),
  L.map(delay(1000)),
  L.map((a) => a + 10),
  L.take(3),
  each(log)
);
// 1초 후 10
// 2초 후 11
// 3초 후 12

Promise/async/await

FxJS의 함수들은 비동기를 잘 다룹니다. Promise의 프로토콜을 잘 따르고 있어 async/await과도 함께 사용할 수 있습니다.

// L.interval = time => L.map(delay(time), L.range(Infinity));

await go(
  L.interval(1000),
  L.map((a) => a + 30),
  L.takeUntil((a) => a == 33),
  each(log)
);
// 1초 후 30
// 2초 후 31
// 3초 후 32
// 4초 후 33

const res = await go(
  L.interval(1000),
  L.map((a) => a + 20),
  L.takeWhile((a) => a < 23),
  L.map(tap(log)),
  reduce(add)
);
// 5초 후 20
// 6초 후 21
// 7초 후 22

log(res);
// 63

Concurrency

C 네임스페이스의 함수를 통해 동시/병렬적인 평가가 가능합니다.

await map(getPage, range(1, 5));
// 4초 후
// [page1, page2, page3, page4]

const pages = await C.map(getPage, range(1, 5));
// 1초 후
// [page1, page2, page3, page4]

L 함수들로 지연해둔 함수열을 C 함수로 동시에 평가할 수도 있습니다. 이런 방식은 Clojure Reducers와 비슷합니다.

go(
  range(1, 5),
  map(getPage),
  filter((page) => page.line > 50),
  map(getWords),
  flat,
  countBy(identity),
  log
);
// 4초 후
// { html: 78, css: 36, is: 192 ... }

go(
  L.range(1, 5),
  L.map(getPage),
  L.filter((page) => page.line > 50),
  L.map(getWords),
  C.takeAll, // 4개 페이지 동시 요청
  flat,
  countBy(identity),
  log
);
// 1초 후
// { html: 78, css: 36, is: 192 ... }

go(
  L.range(1, 5),
  L.map(getPage),
  L.filter((page) => page.line > 50),
  L.map(getWords),
  C.takeAll(2), // 2개 페이지씩 나눠서 동시 요청
  flat,
  countBy(identity),
  log
);
// 2초 후
// { html: 78, css: 36, is: 192 ... }

Error handling

FxJS는 자바스크립트의 기본 프로토콜을 지키고 있기 때문에 자바스크립트 표준 에러 핸들링을 사용할 수 있습니다.

const b = go(
  0,
  (a) => a + 1,
  (a) => a + 10,
  (a) => a + 100
);

console.log(b);
// 111

try {
  const b = go(
    0,
    (a) => {
      throw { hi: "ho" };
    },
    (a) => a + 10,
    (a) => a + 100
  );

  console.log(b);
} catch (c) {
  console.log(c);
}
// { hi: 'ho' }

async/await와 try/catch를 사용하여 비동기 에러 핸들링을 할 수 있습니다. 표준적인 에러 핸들링을 사용하기 때문에 여러 라이브러리들과 함께 사용하기 좋습니다.

const b = await go(
  0,
  (a) => Promise.resolve(a + 1),
  (a) => a + 10,
  (a) => a + 100
);

console.log(b);
// 111

try {
  const b = await go(
    0,
    (a) => Promise.resolve(a + 1),
    (a) => Promise.reject({ hi: "ho" }),
    (a) => a + 100
  );

  console.log(b);
} catch (c) {
  console.log(c);
}
// { hi: 'ho' }

API

Extention Libraries

위의 라이브러리들은 FxJS를 기반으로 만들어졌습니다. FxSQL과 FxDOM은 각각 SQL과 DOM을 함수형 API를 통해 다룰 수 있는 라이브러리 입니다. 그리고 FxContrib는 FxJS와 FxDOM, FxSQL의 Contributor를 위해 만들어 졌습니다.