02.07 TIL


私は関数プログラミングとJavaScript ES 6+を勉強しています.

L.filter

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

go(
  [1, 2, 3, 4, 5, 6],
  L.map((v) => Promise.resolve(v * v)),
  L.filter((v) => v % 2),
  take(2),
  log,
); // []
現在、L.filter関数ではPromiseに渡される値は受け入れられません.
L.filter = curry(function* (f, iter) {
  for (const v of iter) {
    const value = go1(v, f);
    if (value instanceof Promise)
      yield value.then((a) => (a ? v : Promise.reject()));
    else if (value) yield v;
  }
});

go(
  [1, 2, 3, 4, 5, 6],
  L.map((v) => Promise.resolve(v * v)),
  L.filter((v) => v % 2),
  take(2),
  log,
); // Uncaught (in promise) Symbol(nop)
L.filter関数では、iterableがPromiseでない場合に即座に屈服し、PromiseではPromiseをtake関数に渡してtake関数で値を使用することを実現している.
また,PromiseはPromiseであるが,条件を満たさない値は後で伝達されない.拒否(nop)を使用しました.
Symbolを使用する理由は、Symbolの特性が他のプログラムと競合しないため、処理する必要のない値であるためです。

これでtake関数からfilter処理の拒否値を受信すれば終了する.
const take = curry((limit, iter) => {
  const res = [];
  iter = iter[Symbol.iterator]();
  let cur;
  return (function recur() {
    while (!(cur = iter.next()).done) {
      const value = cur.value;
      if (value instanceof Promise)
        return value
          .then((v) => ((res.push(v), res).length === limit ? res : recur()))
          .catch((e) => (e === nop ? recur() : Promise.reject(e)));

      res.push(value);
      if (res.length === limit) return res;
    }

    return res;
  })();
});

go(
  [1, 2, 3, 4, 5, 6],
  L.map((v) => Promise.resolve(v * v)),
  L.filter((v) => v % 2),
  take(2),
  log,
); // [1, 9]
これでL.filterはPromiseを受信できます.
PromiseはKleisli Componentの観点から、拒否された値は後のthenを無視してcatchに移動するため、L.filterの後に他の関数があっても処理をスキップしません.

reduceサポートnop

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

  return go1(acc, function recur(acc) {
    let cur;
    while (!(cur = iter.next()).done) {
      const value = cur.value;
      acc = f(acc, value);
      if (acc instanceof Promise) return acc.then(recur);
    }
    return acc;
  });
});
現在のreduce関数はPromiseです.拒否()に変換された値を処理できないため、変更しようとします.
// 추가된 부분
const reduceInner = (acc, value, f) =>
  value instanceof Promise
    ? value.then(
        (v) => f(acc, v),
        (e) => (e === nop ? acc : Promise.reject(e)),
      )
    : f(acc, value);

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

  return go1(acc, function recur(acc) {
    let cur;
    while (!(cur = iter.next()).done) {
      acc = reduceInner(acc, cur.value, f);
      if (acc instanceof Promise) return acc.then(recur);
    }
    return acc;
  });
});
反復器の値はPromiseです.拒否()が発生しないように、ReduceInner関数でエラーを処理できます.