Source: list/inf.js

/**
 * maryamyriameliamurphies.js
 * A library of Haskell-style morphisms ported to ES2015 JavaScript.
 *
 * list/inf.js
 *
 * @file Infinite list functions.
 * @license ISC
 */

/** @module list/inf */

import {
  partial,
  id
} from '../base';

import {
  list,
  listRangeLazyBy,
  cons,
  isList,
  isEmpty,
  head,
  tail,
  take
} from '../list';

import {error} from '../error';

/**
 * Generate an infinite `List`. Use `listInfBy` to supply your own step function.
 * @param {*} start - The value with which to start the `List`
 * @returns {List} An infinite `List` of consecutive values, incremented from `start`
 * @kind function
 */
export const listInf = start => listInfBy(start, (x => x + 1));

/**
 * Generate an infinite `List`, incremented using a given step function.
 * @param {*} start - The value with which to start the `List`
 * @param {Function} step - A unary step function
 * @returns {List} An infinite `List` of consecutive values, incremented from `start`
 * @kind function
 */
export const listInfBy = (start, step) => {
  const listInfBy_ = (start, step) => listRangeLazyBy(start, Infinity, step);
  return partial(listInfBy_, start, step);
}

/**
 * Return an infinite `List` of repeated applications of a function to a value.
 * <br>`Haskell> iterate :: (a -> a) -> a -> [a]`
 * @param {Function} f - The function to apply
 * @param {*} x - The value to apply the function to
 * @returns {List} An infinite `List` of repeated applications of `f` to `x`
 * @kind function
 * @example
 * const f = x => x * 2;
 * const lst = iterate(f, 1);
 * take(10, lst);             // => [1:2:4:8:16:32:64:128:256:512:[]]
 * index(lst, 10);            // => 1024
 */
export const iterate = (f, x) => {
  const iterate_ = (f, x) => listInfBy(x, (x => f(x)));
  return partial(iterate_, f, x);
}

/**
 * Build an infinite `List` of identical values.
 * <br>`Haskell> repeat :: a -> [a]`
 * @param {*} a - The value to repeat
 * @returns {List} The infinite `List` of repeated values
 * @kind function
 * @example
 * const lst = repeat(3);
 * take(10, lst);         // => [3:3:3:3:3:3:3:3:3:3:[]]
 * index(lst, 100);       // => 3
 */
export const repeat = a => cons(a)(listInfBy(a, id));

/**
 * Return a `List` of a specified length in which every value is the same.
 * <br>`Haskell> replicate :: Int -> a -> [a]`
 * @param {number} n - The length of the `List`
 * @param {*} x - The value to replicate
 * @returns {List} The `List` of values
 * @kind function
 * @example
 * replicate(10, 3); // => [3:3:3:3:3:3:3:3:3:3:[]]
 */
export const replicate = (n, x) => {
  const replicate_ = (n, x) => take(n, repeat(x));
  return partial(replicate_, n, x);
}

/**
 * Return the infinite repetition of a `List` (i.e. the "identity" of infinite lists).
 * <br>`Haskell> cycle :: [a] -> [a]`
 * @param {List} as - A finite `List`
 * @returns {List} A circular `List`, the original `List` infinitely repeated
 * @kind function
 * @example
 * const lst = list(1,2,3);
 * const c = cycle(lst);
 * take(9, c);              // => [1:2:3:1:2:3:1:2:3:[]]
 * index(c, 100);           // => 2
 */
export const cycle = as => {
  if (isList(as) === false) { return error.listError(as, cycle); }
  if (isEmpty(as)) { return error.emptyList(as, cycle); }
  let x = head(as);
  let xs = tail(as);
  const c = list(x);
  /* eslint no-constant-condition: ["error", { "checkLoops": false }] */
  const listGenerator = function* () {
    do {
      x = isEmpty(xs) ? head(as) : head(xs);
      xs = isEmpty(xs) ? tail(as) : tail(xs);
      yield list(x);
    } while (true);
  }
  const gen = listGenerator();
  const handler = {
    get: function (target, prop) {
      if (prop === `tail` && isEmpty(tail(target))) {
        const next = gen.next();
        target[prop] = () => new Proxy(next.value, handler);
      }
      return target[prop];
    }
  };
  const proxy = new Proxy(c, handler);
  return proxy;
}