graph-ecs/src/fn.js

111 lines
3.8 KiB
JavaScript

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))