Source: list/folds.js

/**
 * maryamyriameliamurphies.js
 * A library of Haskell-style morphisms ported to ES2015 JavaScript.
 *
 * list/folds.js
 *
 * @file Special folds for lists.
 * @license ISC
 */

/** @module list/folds */

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

import {
  emptyList,
  listAppend,
  head,
  tail,
  isList,
  isEmpty,
  map
} from '../list';

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

/**
 * Concatenate the elements in a container of lists. Currently, this function only works on `List`
 * objects, though it should in the future work on all `Foldable` types.
 * <br>`Haskell> concat :: Foldable t => t [a] -> [a]`
 * @param {List} xss - A `List` of lists
 * @returns {List} The concatenated `List`
 * @kind function
 * @example
 * const lst1 = list(1,2,3);
 * const lst2 = list(4,5,6);
 * const lst3 = list(7,8,9);
 * const xss = list(lst1, lst2, lst3); // [[1:2:3:[]]:[4:5:6:[]]:[7:8:9:[]]:[]]
 * concat(xss);                        // => [1:2:3:4:5:6:7:8:9:[]]
 */
export const concat = xss => {
  if (isList(xss)) {
    if (isEmpty(xss)) { return emptyList; }
    const x = head(xss);
    const xs = tail(xss);
    return isList(x) ? listAppend(x, concat(xs)) : error.listError(x, concat);
  }
  return error.listError(xss, concat);
}

/**
 * Map a function that takes a value and returns a `List` over a `List` of values and concatenate
 * the resulting list. In the future, should work on all `Foldable` types.
 * <br>`Haskell> concatMap :: Foldable t => (a -> [b]) -> t a -> [b]`
 * @param {Function} f - The function to map
 * @param {List} xs - The `List` to map over
 * @returns {List} The `List` of results of mapping `f` over `xs`, concatenated
 * @kind function
 * @example
 * const f = x => list(x * 3);
 * const lst = list(1,2,3);    // [1:2:3:[]]
 * map(f, lst);                // => [[3:[]]:[6:[]]:[9:[]]:[]]
 * concatMap(f, lst);          // => [3:6:9:[]]
 */
export const concatMap = (f, xs) => {
  const concatMap_ = (f, xs) => concat(map(f, xs));
  return partial(concatMap_, f, xs);
}