/**
* maryamyriameliamurphies.js
* A library of Haskell-style morphisms ported to ES2015 JavaScript.
*
* monoid.js
*
* @file Monoid type class.
* @license ISC
*/
/** @module monoid */
import {partial} from './base';
import {foldr} from './foldable';
import {
defines,
dataType,
typeCheck
} from './type';
import {error} from './error';
/**
* A `Monoid` is a type with an associative binary operation that has an identity. In plainer
* language, a monoid is any type that has an "empty" value that, when "appended" to any other value
* of that type, equals that same value. For example, an integer is a monoid, because any integer
* added to 0, the "empty" value, equals that integer. Likewise, a list is a monoid, because any
* list appended to the empty list equals the original list. Monoids must define `mempty` and
* `mappend` methods.
* @param {*} - Any object
* @kind function
* @returns {boolean} `true` if an object is an instance of `Monoid` and `false` otherwise
*/
export const Monoid = defines(`mempty`, `mappend`);
/**
* Return the identity (or "empty") value for the monoid.
* <br>`Haskell> mempty :: a`
* @param {Object} a - Any monoid
* @returns {Object} Identity value for the monoid
* @kind function
* @example
* const mb = just(1);
* const tup = tuple(1,2);
* const lst = list(1,2,3);
* mempty(mb); // => Nothing
* mempty(tup); // => ()
* mempty(lst); // => [[]]
*/
export const mempty = a => Monoid(a) ? dataType(a).mempty(a) : error.typeError(a, mempty);
/**
* Perform an associative operation (similar to appending to a list) on two monoids.
* <br>`Haskell> mappend :: a -> a -> a`
* @param {Object} a - Any monoid
* @param {Object} b - Any monoid
* @returns {Object} A new monoid of the same type, the result of the associative operation
* @kind function
* @example
* const l1 = list(1,2,3); // => [1:2:3:[]]
* const l2 = list(4,5,6); // => [4:5:6:[]]
* const l3 = list(7,8,9); // => [7:8:9:[]]
* mappend(mempty(l1), l1); // => [1:2:3:[]]
* mappend(l1, mappend(l2, l3)); // => [1:2:3:4:5:6:7:8:9:[]]
* mappend(mappend(l1, l2), l3); // => [1:2:3:4:5:6:7:8:9:[]]
*/
export const mappend = (a, b) => {
const mappend_ = (a, b) => {
if (typeCheck(a, b)) {
return Monoid(a) ? dataType(a).mappend(a, b) : error.typeError(a, mappend);
}
return error.typeMismatch(a, b, mappend);
}
return partial(mappend_, a, b);
}
/**
* Fold a list using the monoid. Concatenates a list of monoids into a single list. Since lists
* themselves are monoids, for example, `mconcat` will flatten a list of lists into a single list.
* <br>`Haskell> mconcat :: [a] -> a`
* @param {Object} a - Any monoid
* @returns {Object} A new monoid of the same type, the result of the concatenation
* @kind function
* @example
* const l1 = list(1,2,3); // => [1:2:3:[]]
* const l2 = list(4,5,6); // => [4:5:6:[]]
* const l3 = list(7,8,9); // => [7:8:9:[]]
* const ls = list(l1,l2,l3); // => [[1:2:3:[]]:[4:5:6:[]]:[7:8:9:[]]:[]]
* mconcat(ls); // => [1:2:3:4:5:6:7:8:9:[]]
*/
export const mconcat = a => foldr(mappend, mempty(a), a);