UNPKG

meows

Version:
179 lines (177 loc) 6.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const types_1 = require("./types"); const types_2 = require("./types"); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Functions /** * Takes a sequence of functions and composes them from right to left, where * given an array of functions such as `[λa, λb, λc]`, return a new function * `(...args) => λa(λb(λc(...args)))`. * * @example * let add2 = x => x + 2 * let mul3 = x => x * 3 * let sub1 = x => x - 1 * let λa = compose(add2, mul3) // add2(mul3(...a)) * let λb = compose(mul3, add2) // mul3(add2(...a)) * let λc = compose(add2, mul3, sub1) // add2(mul3(sub1(...a))) * λa(0) // => 2 * λb(0) // => 6 * λc(0) // => -1 */ exports.compose = (...fns) => fns.reduce((f, g) => (...a) => f(g(...a))); /** * Takes a sequence of functions and composes them from left to right, where * given an array of functions such as `[λa, λb, λc]`, return a new function * `(...args) => λc(λb(λa(...args)))`. * * @example * let add2 = x => x + 2 * let mul3 = x => x * 3 * let sub1 = x => x - 1 * let λa = pipe(add2, mul3) // mul3(add2(...a)) * let λb = pipe(mul3, add2) // add2(mul3(...a)) * let λc = pipe(add2, mul3, sub1) // sub1(mul3(add2(...a))) * λa(0) // => 6 * λb(0) // => 2 * λc(0) // => 5 */ exports.pipe = (...fns) => fns.reduceRight((f, g) => (...a) => f(g(...a))); /** * Given a lambda `λ` and two arrays of non-zero length, `arr1` and `arr2`, * creates new list where each new value indexed at position `i` corresponds to * the result of `λ(arr1[i], arr2[i])`. * * @example * const triangles = [0, 1, 3, 6, 10] * const squares = [0, 1, 4, 9, 16] * const naturals = [0, 1, 2, 3] * const minus = (a, b) => a - b * * zipWith(minus, squares, triangles) // => [0, 0, 1, 3, 6] * zipWith(minus, triangles, naturals) // => [0, 0, 1, 3] * zipWith(minus, [], triangles) // => [] */ function zipWith(λ, arr1, arr2) { if (arr1.length === 0 || arr2.length === 0) return []; return [λ(arr1[0], arr2[0])].concat(zipWith(λ, arr1.slice(1), arr2.slice(1))); } exports.zipWith = zipWith; /** * @example * [true, true, false].filter(identity) // => [true, true] */ exports.identity = x => x; /** * Given a lambda and a value `x`, run the lambda with optional arguments as a * side effect, and then return the original given value `x`. * * @example * function sayHi(msg = 'hi') { console.log(msg) } * effectId(sayHi, 42) // => logs 'hi' and returns 42 * effectId(sayHi, 42, 'yo') // => logs 'yo' and returns 42 */ function effectId(λ, x, ...arg) { if (typeof λ !== 'function') throw new TypeError(`effectId() requires a function, but got: ${λ}.`); λ(...arg); return x; } exports.effectId = effectId; /** * Count the number of array elements which pass a boolean function. * @example * const v1 = [2, 4, 6, 8] * const v2 = [2, 4, 6, 9] * const even = x => x % 2 === 0 * * count(even, v1) // => 4 * count(even, v2) // => 1 * count(even, []) // => 0 */ function count(λ, arr) { if (!Array.isArray(arr)) throw new TypeError(`count(λ, arr) requires an array, but got: ${arr}.`); if (typeof λ !== 'function') throw new TypeError(`count(λ, arr) requires a function, but got: ${λ}.`); return arr.filter(λ).length; } exports.count = count; /** * Given λ → returns `(compose not λ)`. * @example * const notArray = not(Array.isArray) * notArray([]) // => false * notArray({}) // => true */ exports.not = (λ) => (...arg) => !λ(...arg); /** * Checks if an array is a **non-empty array** of items that would all pass your * boolean function. This is unlike the normal convention to pass on empty * arrays. * * @example * always(even, [0, 2, 4]) // => true * always(even, [0, 1, 2]) // => false * always(even, []) // => false, fails on empty array! */ function always(λ, arr) { if (!Array.isArray(arr) || typeof λ !== 'function') throw new TypeError(`always() requires a boolean function and array of arguments, but got:` + `\n arg[0]: ${types_1.typeName(λ)}` + `\n arg[1]: ${types_1.typeName(arr)}`); return Boolean(arr.length) && arr.every(λ); } exports.always = always; /** * Coerces values to `true`. If input is a function, `truthy` will run the * function with optional arguments for side effects before returning `true`. * @example * truthy(console.log('hi')) && 42 // logs 'hi' and returns 42 * * const say = (msg='hi') => console.log(msg) * truthy(say) // logs 'hi' and returns true * truthy(say, 'yo') // logs 'yo' and returns true */ function truthy(λ, ...arg) { typeof λ === 'function' && void λ(...arg); return true; } exports.truthy = truthy; /** * Coerces values to `false`. If input is a function, `falsy` will run the * function with optional arguments for side effects before returning `false` * * @example * falsy(console.log('hi')) || 42 // logs 'hi' and returns 42 * * function say(msg='hi') { console.log(msg) } * falsy(say) // logs 'hi' and returns false * falsy(say, 'yo') // logs 'yo' and returns false */ function falsy(λ, ...arg) { typeof λ === 'function' && void λ(...arg); return false; } exports.falsy = falsy; /** * Checks if a non-empty array of functions all pass another non-empty array of * values. Returns `false` on any empty array. * * @example * allPassAll([isEven, isPositive, isSafe], [2, 4, 6, 8]) // => true */ function allPassAll(tests, values, throwOnEmpty = false) { if (!tests.every(types_2.isType.fn)) throw new TypeError(`allPassAll() requires an array of only functions, but got: ${tests}.`); if (!always(Array.isArray, [tests, values])) throw new TypeError(`allPassAll() requires two arrays, but got: ${tests}, ${values}.`); if (throwOnEmpty && [tests, values].some(types_1.isEmpty.arr)) throw new TypeError(`allPassAll() rejects empty arrays due to optional flag, but got: ${tests}, ${values}.`); return [tests, values].some(types_1.isEmpty.arr) ? false : values.every(val => tests.every(λ => λ(val))); } exports.allPassAll = allPassAll;