如何在严格评估的设置中编码corecursion / codata?

问题描述 投票:1回答:1

Corecursion意味着在每次迭代中调用大于或等于以前的数据的数据。 Corecursion在codata上工作,codata是递归定义的值。不幸的是,在严格评估的语言中无法进行值递归。不过,我们可以处理明显的重击:

const Defer = thunk =>
  ({get runDefer() {return thunk()}})

const app = f => x => f(x);

const fibs = app(x_ => y_ => {
  const go = x => y =>
    Defer(() =>
      [x, go(y) (x + y)]);

  return go(x_) (y_).runDefer;
}) (1) (1);

const take = n => codata => {
  const go = ([x, tx], acc, i) =>
    i === n
      ? acc
      : go(tx.runDefer, acc.concat(x), i + 1);

  return go(codata, [], 0);
};

console.log(
  take(10) (fibs));

尽管这可以按预期工作,但该方法似乎很尴尬。尤其是那对丑陋的元组让我很烦。有没有更自然的方法来处理JS中的corecursion / codata?

javascript functional-programming lazy-evaluation corecursion codata
1个回答
0
投票

我将在数据构造函数本身中对代码进行编码。例如,考虑。

// whnf :: Object -> Object
const whnf = obj => {
    for (const [key, val] of Object.entries(obj)) {
        if (typeof val === "function" && val.length === 0) {
            Object.defineProperty(obj, key, {
                get: () => Object.defineProperty(obj, key, {
                    value: val()
                })[key]
            });
        }
    }

    return obj;
};

// empty :: List a
const empty = null;

// cons :: (a, List a) -> List a
const cons = (head, tail) => whnf({ head, tail });

// fibs :: List Int
const fibs = cons(0, cons(1, () => next(fibs, fibs.tail)));

// next :: (List Int, List Int) -> List Int
const next = (xs, ys) => cons(xs.head + ys.head, () => next(xs.tail, ys.tail));

// take :: (Int, List a) -> List a
const take = (n, xs) => n === 0 ? empty : cons(xs.head, () => take(n - 1, xs.tail));

// toArray :: List a -> [a]
const toArray = xs => xs === empty ? [] : [ xs.head, ...toArray(xs.tail) ];

// [0,1,1,2,3,5,8,13,21,34]
console.log(toArray(take(10, fibs)));

这样,我们可以在weak head normal form中编码懒惰。好处是,消费者不知道给定数据结构的特定字段是惰性字段还是严格字段,因此无需关心。

© www.soinside.com 2019 - 2024. All rights reserved.