import { Result } from './result.js' export const identity = x => x // functions export function curryN(arity, func) { return function curried(...args) { if (args.length >= arity) { return func.apply(this, args) } else { return function(...args2) { return curried.apply(this, args.concat(args2)) } } } } export const curry = fn => curryN(fn.length, fn) export const flip = fn => curry((a, b) => fn(b, a)) export const tap = curry((fn, val) => { fn(val) return val }) export const always = x => () => x export const pipe = (...f) => v => reduce(applyTo, v, f) export const apply = fn => args => fn.apply(null, args) export const applyTo = curry((v, fn) => fn(v)) export const useWith = curry((fn, tfns) => curryN( tfns.length, (...args) => pipe(enumerate, map(([i, a]) => tfns[i](a)), fn)(args) )) export const converge = (fn, [head, ...rest]) => curryN( head.length, (...args) => pipe(...rest, fn)(head(...args)) ) export const diverge = (fn, tfns) => pipe(applyTo, flip(map)(tfns), apply(fn)) // types export const isArray = Array.isArray export const isNil = v => !v export const is = curry((type, value) => value instanceof type) // branching export const ifElse = curry((p, ft, ff, v) => p(v) ? ft(v) : ff(v)) export const when = curry((p, f, v) => ifElse(p, f, identity, v)) export const unless = curry((p, f, v) => ifElse(p, identity, f, v)) // predicates export const and = (...booleans) => booleans.every(identity) export const or = (...booleans) => booleans.some(identity) export const isOk = value => !is(Result, value) || value.isOk() // classes export const construct = Type => args => new Type(...args) // strings export const split = curry((delim, v) => v.split(delim)) // arrays export const length = v => v.length export const len = length export const of = unless(isArray, value => ([value])) export const concat = curry((a, b) => b.concat(a)) export const take = curry((n, value) => value.slice(0, n)) export const drop = curry((n, value) => value.slice(n)) export const nth = curry((index, value) => value[index]) export const head = nth(0) export const tail = drop(1) export const map = curry((fn, v) => v.map(fn)) export const filter = curry((fn, v) => v.filter(fn)) export const reduce = curry((fn, init, v) => v.reduce(fn, init)) export const find = curry((fn, v) => v.find(fn)) export const join = curry((sep, v) => v.join(sep)) export const rev = v => v.slice().reverse() export const prepend = curry((value, iter) => [value, ...iter]) export const append = curry((value, iter) => [...iter, value]) export const enumerate = value => value.map((v, i) => ([i, v])) export const flatten = curry((n, v) => v.flat(n)) export const update = curry((index, value, arr) => arr.toSpliced(index, 1, value)) // objects const makePath = unless(isArray, split('.')) export const assoc = curry((key, value, obj) => ({ ...obj, [key]: value })) export const assocPath = curry((key, value, obj) => pipe( makePath, rev, reduce((acc, val) => fromEntries([[val, acc]]), value), mergeLeft(obj) )(key)) export const prop = curry((key, obj) => obj && key && obj[key]) export const path = curry((key, obj) => reduce(flip(prop), obj, makePath(key))) export const mergeLeft = curry((a, b) => ({ ...b, ...a })) export const mergeRight = flip(mergeLeft) export const keys = Object.keys export const values = Object.values export const entries = Object.entries export const fromEntries = Object.fromEntries // lenses export const lens = curry((get, set) => ({ get, set })) export const lensPath = path => lens(path(path), assocPath(path)) export const lensIndex = index => lens(nth(index), update(index)) export const view = curry(({ get }, obj) => get(obj)) export const set = curry((l, value, obj) => over(l, always(value), obj)) export const over = curry(({ get, set }, fn, obj) => set(fn(get(obj)), obj))